import { getUserEncryptionKey } from "../keyManager.js";
import System from "../System.js";
import SeriesCharacterRepo, { SeriesCharacterAttributeResult, SeriesCharacterResult } from "../repositories/series-character.repo.js";
import RemovedItem from "./RemovedItem.js";

export type CharacterCategory = 'Main' | 'Secondary' | 'Recurring';

export interface SeriesCharacterPropsPost {
    id: string | null;
    name: string;
    lastName: string;
    nickname: string;
    age: number | null;
    gender: string;
    species: string;
    nationality: string;
    status: string;
    category: CharacterCategory;
    title: string;
    image: string;
    physical: { name: string }[];
    psychological: { name: string }[];
    relations: { name: string }[];
    skills: { name: string }[];
    weaknesses: { name: string }[];
    strengths: { name: string }[];
    goals: { name: string }[];
    motivations: { name: string }[];
    arc: { name: string }[];
    secrets: { name: string }[];
    fears: { name: string }[];
    flaws: { name: string }[];
    beliefs: { name: string }[];
    conflicts: { name: string }[];
    quotes: { name: string }[];
    distinguishingMarks: { name: string }[];
    items: { name: string }[];
    affiliations: { name: string }[];
    role: string;
    biography?: string;
    history?: string;
    speechPattern?: string;
    catchphrase?: string;
    residence?: string;
    notes?: string;
    color?: string;
}

export interface SeriesCharacterListProps {
    id: string;
    name: string;
    lastName: string;
    nickname: string;
    age: number | null;
    gender: string;
    species: string;
    nationality: string;
    status: string;
    title: string;
    category: string;
    image: string;
    role: string;
    biography: string;
    history: string;
    speechPattern: string;
    catchphrase: string;
    residence: string;
    notes: string;
    color: string;
}

export interface SeriesAttribute {
    id: string;
    name: string;
}

export interface CharacterAttributesResponse {
    attributes: SeriesAttribute[];
}

export default class SeriesCharacter {
    /**
     * Retrieves a list of characters for a specific series owned by a user.
     * @param userId - The unique identifier of the user
     * @param seriesId - The unique identifier of the series
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns Characters list
     */
    public static getCharacterList(userId: string, seriesId: string, lang: 'fr' | 'en' = 'fr'): SeriesCharacterListProps[] {
        const characters: SeriesCharacterResult[] = SeriesCharacterRepo.fetchCharacters(userId, seriesId, lang);
        if (!characters || characters.length === 0) {
            return [];
        }

        const userKey: string = getUserEncryptionKey(userId);

        return characters.map((character: SeriesCharacterResult): SeriesCharacterListProps => ({
            id: character.character_id,
            name: character.first_name ? System.decryptDataWithUserKey(character.first_name, userKey) : '',
            lastName: character.last_name ? System.decryptDataWithUserKey(character.last_name, userKey) : '',
            nickname: character.nickname ? System.decryptDataWithUserKey(character.nickname, userKey) : '',
            age: character.age ? parseInt(System.decryptDataWithUserKey(character.age, userKey), 10) : null,
            gender: character.gender ? System.decryptDataWithUserKey(character.gender, userKey) : '',
            species: character.species ? System.decryptDataWithUserKey(character.species, userKey) : '',
            nationality: character.nationality ? System.decryptDataWithUserKey(character.nationality, userKey) : '',
            status: character.status ? System.decryptDataWithUserKey(character.status, userKey) : 'alive',
            title: character.title ? System.decryptDataWithUserKey(character.title, userKey) : '',
            category: character.category ? System.decryptDataWithUserKey(character.category, userKey) : '',
            image: character.image ? System.decryptDataWithUserKey(character.image, userKey) : '',
            role: character.role ? System.decryptDataWithUserKey(character.role, userKey) : '',
            biography: character.biography ? System.decryptDataWithUserKey(character.biography, userKey) : '',
            history: character.history ? System.decryptDataWithUserKey(character.history, userKey) : '',
            speechPattern: character.speech_pattern ? System.decryptDataWithUserKey(character.speech_pattern, userKey) : '',
            catchphrase: character.catchphrase ? System.decryptDataWithUserKey(character.catchphrase, userKey) : '',
            residence: character.residence ? System.decryptDataWithUserKey(character.residence, userKey) : '',
            notes: character.notes ? System.decryptDataWithUserKey(character.notes, userKey) : '',
            color: character.color ? System.decryptDataWithUserKey(character.color, userKey) : '',
        }));
    }

    /**
     * Adds a new character to a series with all its attributes.
     * @param userId - The unique identifier of the user
     * @param character - The character data to create
     * @param seriesId - The unique identifier of the series
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns The newly created character's ID
     */
    public static addNewCharacter(userId: string, character: SeriesCharacterPropsPost, seriesId: string, lang: 'fr' | 'en' = 'fr'): string {
        const userKey: string = getUserEncryptionKey(userId);
        const characterId: string = System.createUniqueId();
        const encryptedName: string = System.encryptDataWithUserKey(character.name, userKey);
        const encryptedLastName: string | null = character.lastName ? System.encryptDataWithUserKey(character.lastName, userKey) : null;
        const encryptedNickname: string | null = character.nickname ? System.encryptDataWithUserKey(character.nickname, userKey) : null;
        const encryptedAge: string | null = character.age !== null ? System.encryptDataWithUserKey(String(character.age), userKey) : null;
        const encryptedGender: string | null = character.gender ? System.encryptDataWithUserKey(character.gender, userKey) : null;
        const encryptedSpecies: string | null = character.species ? System.encryptDataWithUserKey(character.species, userKey) : null;
        const encryptedNationality: string | null = character.nationality ? System.encryptDataWithUserKey(character.nationality, userKey) : null;
        const encryptedStatus: string | null = character.status ? System.encryptDataWithUserKey(character.status, userKey) : null;
        const encryptedTitle: string | null = character.title ? System.encryptDataWithUserKey(character.title, userKey) : null;
        const encryptedCategory: string | null = character.category ? System.encryptDataWithUserKey(character.category, userKey) : null;
        const encryptedImage: string | null = character.image ? System.encryptDataWithUserKey(character.image, userKey) : null;
        const encryptedRole: string | null = character.role ? System.encryptDataWithUserKey(character.role, userKey) : null;
        const encryptedBiography: string | null = character.biography ? System.encryptDataWithUserKey(character.biography, userKey) : null;
        const encryptedHistory: string | null = character.history ? System.encryptDataWithUserKey(character.history, userKey) : null;
        const encryptedSpeechPattern: string | null = character.speechPattern ? System.encryptDataWithUserKey(character.speechPattern, userKey) : null;
        const encryptedCatchphrase: string | null = character.catchphrase ? System.encryptDataWithUserKey(character.catchphrase, userKey) : null;
        const encryptedResidence: string | null = character.residence ? System.encryptDataWithUserKey(character.residence, userKey) : null;
        const encryptedNotes: string | null = character.notes ? System.encryptDataWithUserKey(character.notes, userKey) : null;
        const encryptedColor: string | null = character.color ? System.encryptDataWithUserKey(character.color, userKey) : null;

        SeriesCharacterRepo.addNewCharacter(userId, characterId, encryptedName, encryptedLastName, encryptedNickname, encryptedAge, encryptedGender, encryptedSpecies, encryptedNationality, encryptedStatus, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, encryptedSpeechPattern, encryptedCatchphrase, encryptedResidence, encryptedNotes, encryptedColor, seriesId, lang);

        const attributeKeys: string[] = Object.keys(character);
        for (const attributeKey of attributeKeys) {
            const attributeValue = character[attributeKey as keyof SeriesCharacterPropsPost];
            if (Array.isArray(attributeValue)) {
                const attributeArray: { name: string }[] = attributeValue;
                if (attributeArray.length > 0) {
                    for (const attributeItem of attributeArray) {
                        const attributeType: string = attributeKey;
                        const attributeName: string = attributeItem.name;
                        this.addNewAttribute(characterId, userId, attributeType, attributeName, lang);
                    }
                }
            }
        }

        return characterId;
    }

    /**
     * Updates an existing character's information and attributes.
     * @param userId - The unique identifier of the user
     * @param character - The updated character data
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the update was successful
     */
    public static updateCharacter(userId: string, character: SeriesCharacterPropsPost, lang: 'fr' | 'en' = 'fr'): boolean {
        if (!character.id) {
            throw new Error(lang === 'fr' ? 'ID du personnage requis.' : 'Character ID required.');
        }

        const exists: boolean = SeriesCharacterRepo.isCharacterExist(userId, character.id, lang);
        if (!exists) {
            throw new Error(lang === 'fr' ? 'Personnage non trouvé.' : 'Character not found.');
        }

        const userKey: string = getUserEncryptionKey(userId);
        const encryptedName: string = System.encryptDataWithUserKey(character.name, userKey);
        const encryptedLastName: string | null = character.lastName ? System.encryptDataWithUserKey(character.lastName, userKey) : null;
        const encryptedNickname: string | null = character.nickname ? System.encryptDataWithUserKey(character.nickname, userKey) : null;
        const encryptedAge: string | null = character.age !== null ? System.encryptDataWithUserKey(String(character.age), userKey) : null;
        const encryptedGender: string | null = character.gender ? System.encryptDataWithUserKey(character.gender, userKey) : null;
        const encryptedSpecies: string | null = character.species ? System.encryptDataWithUserKey(character.species, userKey) : null;
        const encryptedNationality: string | null = character.nationality ? System.encryptDataWithUserKey(character.nationality, userKey) : null;
        const encryptedStatus: string | null = character.status ? System.encryptDataWithUserKey(character.status, userKey) : null;
        const encryptedTitle: string | null = character.title ? System.encryptDataWithUserKey(character.title, userKey) : null;
        const encryptedCategory: string | null = character.category ? System.encryptDataWithUserKey(character.category, userKey) : null;
        const encryptedImage: string | null = character.image ? System.encryptDataWithUserKey(character.image, userKey) : null;
        const encryptedRole: string | null = character.role ? System.encryptDataWithUserKey(character.role, userKey) : null;
        const encryptedBiography: string | null = character.biography ? System.encryptDataWithUserKey(character.biography, userKey) : null;
        const encryptedHistory: string | null = character.history ? System.encryptDataWithUserKey(character.history, userKey) : null;
        const encryptedSpeechPattern: string | null = character.speechPattern ? System.encryptDataWithUserKey(character.speechPattern, userKey) : null;
        const encryptedCatchphrase: string | null = character.catchphrase ? System.encryptDataWithUserKey(character.catchphrase, userKey) : null;
        const encryptedResidence: string | null = character.residence ? System.encryptDataWithUserKey(character.residence, userKey) : null;
        const encryptedNotes: string | null = character.notes ? System.encryptDataWithUserKey(character.notes, userKey) : null;
        const encryptedColor: string | null = character.color ? System.encryptDataWithUserKey(character.color, userKey) : null;

        return SeriesCharacterRepo.updateCharacter(userId, character.id, encryptedName, encryptedLastName, encryptedNickname, encryptedAge, encryptedGender, encryptedSpecies, encryptedNationality, encryptedStatus, encryptedTitle, encryptedCategory, encryptedImage, encryptedRole, encryptedBiography, encryptedHistory, encryptedSpeechPattern, encryptedCatchphrase, encryptedResidence, encryptedNotes, encryptedColor, lang);
    }

    /**
     * Deletes a character from a series.
     * @param userId - The unique identifier of the user
     * @param characterId - The unique identifier of the character
     * @param deletedAt - The timestamp of deletion
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the deletion was successful
     */
    public static deleteCharacter(userId: string, characterId: string, deletedAt: number = System.timeStampInSeconds(), lang: 'fr' | 'en' = 'fr'): boolean {
        const exists: boolean = SeriesCharacterRepo.isCharacterExist(userId, characterId, lang);
        if (!exists) {
            throw new Error(lang === 'fr' ? 'Personnage non trouvé.' : 'Character not found.');
        }
        const deleted: boolean = SeriesCharacterRepo.deleteCharacter(userId, characterId, lang);
        if (deleted) {
            RemovedItem.deleteTracker(userId, null, 'series_characters', characterId, deletedAt, lang);
        }
        return deleted;
    }

    /**
     * Adds a new attribute to a character.
     * @param characterId - The unique identifier of the character
     * @param userId - The unique identifier of the user
     * @param type - The attribute type
     * @param name - The attribute value
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns The attribute ID
     */
    public static addNewAttribute(characterId: string, userId: string, type: string, name: string, lang: 'fr' | 'en' = 'fr'): string {
        const userKey: string = getUserEncryptionKey(userId);
        const attributeId: string = System.createUniqueId();
        const encryptedType: string = System.encryptDataWithUserKey(type, userKey);
        const encryptedName: string = System.encryptDataWithUserKey(name, userKey);

        SeriesCharacterRepo.insertAttribute(attributeId, characterId, userId, encryptedType, encryptedName, lang);

        return attributeId;
    }

    /**
     * Deletes an attribute from a character.
     * @param userId - The unique identifier of the user
     * @param attributeId - The unique identifier of the attribute
     * @param deletedAt - The timestamp of deletion
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns True if the deletion was successful
     */
    public static deleteAttribute(userId: string, attributeId: string, deletedAt: number = System.timeStampInSeconds(), lang: 'fr' | 'en' = 'fr'): boolean {
        const deleted: boolean = SeriesCharacterRepo.deleteAttribute(userId, attributeId, lang);
        if (deleted) {
            RemovedItem.deleteTracker(userId, null, 'series_characters_attributes', attributeId, deletedAt, lang);
        }
        return deleted;
    }

    /**
     * Gets all attributes for a character.
     * @param userId - The unique identifier of the user
     * @param characterId - The unique identifier of the character
     * @param lang - The language for error messages ('fr' or 'en')
     * @returns The character's attributes
     */
    public static getCharacterAttributes(userId: string, characterId: string, lang: 'fr' | 'en' = 'fr'): CharacterAttributesResponse {
        const userKey: string = getUserEncryptionKey(userId);
        const attributesResult: SeriesCharacterAttributeResult[] = SeriesCharacterRepo.fetchAttributes(characterId, userId, lang);

        const attributes: SeriesAttribute[] = attributesResult.map((attr) => ({
            id: attr.attr_id,
            name: System.decryptDataWithUserKey(attr.attribute_value, userKey)
        }));

        return { attributes };
    }
}
