import {BattleTypeTable} from "../database/battle_types";

export interface BattleType {
    id: string,
    name: string,
    emoji: string,
    displayOrder: number,
    offensiveRelationships: {defender: BattleType, damagePercentage: number}[]
    defensiveRelationships: {attacker: BattleType, damagePercentage: number}[]
}

export class BattleTypeManager {
    private cachedBattleTypes: {[key: string]: BattleType} = {}
    private cachedTypeList: BattleType[] = []
    private refreshPromise: Promise<void>|null = null

    private readonly _table: BattleTypeTable

    constructor({battleTypes}: { battleTypes: BattleTypeTable }) {
        this._table = battleTypes
    }

    async refreshCache(): Promise<void> {
        if (this.refreshPromise !== null) {
            await this.refreshPromise
            return
        }

        this.refreshPromise = (async () => {
            const {types, relationships} = await this._table.getTypesAndRelationships()

            const existing = new Set<string>()
            this.cachedTypeList = []

            for (const typeData of types) {
                const battleType: BattleType = this.cachedBattleTypes[typeData.id] ?? {}
                battleType.id = typeData.id
                battleType.name = typeData.name
                battleType.emoji = typeData.emoji
                battleType.displayOrder = typeData.display_order
                battleType.offensiveRelationships = []
                battleType.defensiveRelationships = []
                this.cachedBattleTypes[battleType.id] = battleType
                existing.add(battleType.id)
                this.cachedTypeList.push(battleType)
            }

            for (const key of Object.keys(this.cachedBattleTypes)) {
                if (!existing.has(key)) {
                    delete this.cachedBattleTypes[key]
                }
            }

            for (const relationshipData of relationships) {
                const attacker: BattleType|null = this.cachedBattleTypes[relationshipData.attacker] ?? null
                const defender: BattleType|null = this.cachedBattleTypes[relationshipData.defender] ?? null
                if (attacker === null || defender === null) {
                    throw Error("Battle types named in relationship not found: " + relationshipData.attacker + " vs. " + relationshipData.defender)
                }
                attacker.offensiveRelationships.push({defender: defender, damagePercentage: relationshipData.damage_percentage})
                defender.defensiveRelationships.push({attacker: attacker, damagePercentage: relationshipData.damage_percentage})
            }

            this.cachedTypeList.sort((a, b) => a.displayOrder - b.displayOrder)
        })()

        try {
            await this.refreshPromise
        } finally {
            this.refreshPromise = null
        }
    }

    async getBattleTypes(): Promise<BattleType[]> {
        if (this.refreshPromise !== null) {
            await this.refreshPromise
        }
        return this.cachedTypeList
    }

    async getBattleTypeById(id: string): Promise<BattleType> {
        if (this.refreshPromise !== null) {
            await this.refreshPromise
        }
        const result = this.cachedBattleTypes[id] ?? null
        if (result === null) {
            throw Error("No battle type found with id: " + id)
        }
        return result
    }

    async getBattleTypeByName(name: string): Promise<BattleType> {
        if (this.refreshPromise !== null) {
            await this.refreshPromise
        }
        const result = this.cachedTypeList.find(t => t.name === name) ?? null
        if (result === null) {
            throw Error("No battle type found with name: " + name)
        }
        return result
    }

    async getDamagePercentageByIds(attacker: string, defender: string): Promise<number> {
        if (this.refreshPromise !== null) {
            await this.refreshPromise
        }
        if (Object.hasOwn(this.cachedBattleTypes, attacker) && Object.hasOwn(this.cachedBattleTypes, defender)) {
            return this.cachedBattleTypes[attacker]
                .offensiveRelationships
                .find(({defender: d}) => d.id === defender)?.damagePercentage ?? 100
        } else {
            throw Error("Battle types not found by ID: " + attacker + " vs. " + defender)
        }
    }
}