import UserRepo, {UserAccountQuery, UserInfosQueryResponse} from "../repositories/user.repository.js";
import System from "../System.js";
import Book, {BookProps} from "./Book.js";
import {getUserEncryptionKey} from "../keyManager.js";

interface UserAccount{
    firstName:string;
    lastName:string;
    username:string
    authorName:string;
    email:string;
}

export interface GuideTour {
    [key: string]: boolean;
}

interface BookSummary {
    bookId: string;
    title: string;
    subTitle?: string;
}

export interface UserInfoResponse {
    id: string;
    name: string;
    lastName: string;
    username: string;
    email: string;
    accountVerified: boolean;
    authorName: string;
    groupId: number;
    termsAccepted: boolean;
    guideTour: any[];
    books: BookSummary[];
}

export default class User{

    private readonly id:string;
    private firstName: string;
    private lastName: string;
    private username: string;
    private email: string;
    private accountVerified: boolean;
    private authorName: string;
    private groupId: number;
    private termsAccepted: boolean;

    constructor(id:string){
        this.id = id;
        this.firstName = '';
        this.lastName = '';
        this.username = '';
        this.email = '';
        this.accountVerified = false;
        this.authorName = '';
        this.groupId = 0;
        this.termsAccepted = false;
    }
    
    public async getUserInfos(): Promise<void> {
        const data: UserInfosQueryResponse = UserRepo.fetchUserInfos(this.id);
        const userKey:string = getUserEncryptionKey(this.id)
        this.firstName = System.decryptDataWithUserKey(data.first_name, userKey);
        this.lastName = System.decryptDataWithUserKey(data.last_name, userKey);
        this.username = System.decryptDataWithUserKey(data.username, userKey);
        this.email = System.decryptDataWithUserKey(data.email, userKey);
        this.accountVerified = data.account_verified === 1;
        this.authorName = data.author_name ? System.decryptDataWithUserKey(data.author_name, userKey) : '';
        this.groupId = data.user_group ? data.user_group : 0;
        this.termsAccepted = data.term_accepted === 1;
    }
    
    public static async returnUserInfos(userId: string):Promise<UserInfoResponse> {
        const user: User = new User(userId);
        await user.getUserInfos();
        const books: BookProps[] = await Book.getBooks(userId);
        const guideTour: GuideTour[] = [];
        return {
            id: user.getId(),
            name: user.getFirstName(),
            lastName: user.getLastName(),
            username: user.getUsername(),
            email: user.getEmail(),
            accountVerified: user.isAccountVerified(),
            authorName: user.getAuthorName(),
            groupId: user.getGroupId(),
            termsAccepted: user.isTermsAccepted(),
            guideTour: guideTour,
            books: books.map((book: BookProps):BookSummary => {
                return {
                    bookId: book.id,
                    title: book.title,
                    subTitle: book.subTitle,
                };
            })
        }
    }
    
    public static async addUser(userId:string,firstName: string, lastName: string, username: string, email: string, notEncryptPassword: string, lang: 'fr' | 'en' = 'fr'): Promise<string> {
        const originEmail:string = System.hashElement(email);
        const originUsername:string = System.hashElement(username);
        const userKey: string = getUserEncryptionKey(userId);
        const encryptFirstName: string = System.encryptDataWithUserKey(firstName, userKey);
        const encryptLastName: string = System.encryptDataWithUserKey(lastName, userKey);
        const encryptUsername: string = System.encryptDataWithUserKey(username, userKey);
        const encryptEmail: string = System.encryptDataWithUserKey(email, userKey);
        const originalEmail: string = System.hashElement(email);
        const originalUsername: string = System.hashElement(username);
        return UserRepo.insertUser(userId, encryptFirstName, encryptLastName, encryptUsername, originalUsername, encryptEmail, originalEmail,lang);
    }
    
    public static async updateUserInfos(userKey: string, userId: string, firstName: string, lastName: string, username: string, email: string, authorName?: string, lang: 'fr' | 'en' = 'fr'): Promise<boolean> {
        const encryptFirstName:string = System.encryptDataWithUserKey(firstName,userKey);
        const encryptLastName:string = System.encryptDataWithUserKey(lastName,userKey);
        const encryptUsername:string = System.encryptDataWithUserKey(username,userKey);
        const encryptEmail:string = System.encryptDataWithUserKey(email,userKey);
        const originalEmail:string = System.hashElement(email);
        const originalUsername:string = System.hashElement(username);
        let encryptAuthorName:string = '';
        let originalAuthorName: string = '';
        if (authorName){
            encryptAuthorName = System.encryptDataWithUserKey(authorName,userKey);
            originalAuthorName = System.hashElement(authorName);
        }
        return UserRepo.updateUserInfos(userId, encryptFirstName, encryptLastName, encryptUsername, originalUsername, encryptEmail, originalEmail, originalAuthorName, encryptAuthorName, lang);
    }
    
    public static async getUserAccountInformation(userId: string): Promise<UserAccount> {
        const data: UserAccountQuery = UserRepo.fetchAccountInformation(userId);
        const userKey:string = getUserEncryptionKey(userId)
        const userName: string = data.first_name ? System.decryptDataWithUserKey(data.first_name, userKey) : '';
        const lastName: string = data.last_name ? System.decryptDataWithUserKey(data.last_name, userKey) : '';
        const username: string = data.username ? System.decryptDataWithUserKey(data.username, userKey) : '';
        const authorName: string = data.author_name ? System.decryptDataWithUserKey(data.author_name, userKey) : '';
        const email: string = data.email ? System.decryptDataWithUserKey(data.email, userKey) : '';
        return {
            firstName: userName,
            lastName: lastName,
            username: username,
            authorName: authorName,
            email: email
        };
    }
    
    public getId(): string {
        return this.id;
    }
    
    public getFirstName(): string {
        return this.firstName;
    }
    
    public getLastName(): string {
        return this.lastName;
    }
    
    public getUsername(): string {
        return this.username;
    }
    
    public getEmail(): string {
        return this.email;
    }
    
    public isAccountVerified(): boolean {
        return this.accountVerified;
    }
    
    public isTermsAccepted(): boolean {
        return this.termsAccepted;
    }
    
    public getGroupId(): number {
        return this.groupId;
    }
    
    public getAuthorName(): string {
        return this.authorName;
    }
}
