import * as ism from '$lib/ism';
import * as support from '$lib/data/support';
import * as scenario from '$lib/data/scenario';

function gainTotalStats(gain: ism.GainInfo) {
    return gain.speed.value + gain.stamina.value + gain.power.value + gain.guts.value + gain.wiz.value;
}

export const DEPENDENT = [
    {
        name: 'Total Stats',
        map: (gain: ism.GainInfo, _effects: support.Effect[]) => gainTotalStats(gain),
    },
    {
        name: 'Total Stats + SP',
        map: (gain: ism.GainInfo, _effects: support.Effect[]) => gainTotalStats(gain) + gain.skill_point,
    },
    {
        name: 'Speed',
        map: (gain: ism.GainInfo, _effects: support.Effect[]) => gain.speed.value,
    },
    {
        name: 'Stamina',
        map: (gain: ism.GainInfo, _effects: support.Effect[]) => gain.stamina.value,
    },
    {
        name: 'Power',
        map: (gain: ism.GainInfo, _effects: support.Effect[]) => gain.power.value,
    },
    {
        name: 'Guts',
        map: (gain: ism.GainInfo, _effects: support.Effect[]) => gain.guts.value,
    },
    {
        name: 'Wit',
        map: (gain: ism.GainInfo, _effects: support.Effect[]) => gain.wiz.value,
    },
    {
        name: 'SP',
        map: (gain: ism.GainInfo, _effects: support.Effect[]) => gain.skill_point,
    },
    {
        name: 'Skill Hints',
        map: (gain: ism.GainInfo, effects: support.Effect[]) => {
            // NOTE(zephyr): reduction starts with 1 because we want the value
            // of each hint, not just the bonus from effects
            const hintLevels = effects.reduce((l, e) => e.type === support.EffectType.HintLevels ? l + e.value : l, 1);
            return gain.skill_tips_array.reduce((n, t) => n + Math.ceil(t.level / hintLevels), 0);
        },
    },
    {
        name: 'Skills',
        map: (gain: ism.GainInfo, _effects: support.Effect[]) => gain.skill_tips_array.length,
    },
] as const;

function label(c: ism.Career): string {
    return `${c.end_info.chara_info.single_mode_chara_id} - ${c.end_info.chara_info.start_time}`;
}

export const INDEPENDENT = [
    {
        name: 'Friendship Bonus',
        effect: support.EffectType.FriendshipBonus,
    },
    {
        name: 'Mood Effect',
        effect: support.EffectType.MoodEffect,
    },
    {
        name: 'Speed Bonus',
        effect: support.EffectType.SpeedBonus,
    },
    {
        name: 'Stamina Bonus',
        effect: support.EffectType.StaminaBonus,
    },
    {
        name: 'Power Bonus',
        effect: support.EffectType.PowerBonus,
    },
    {
        name: 'Guts Bonus',
        effect: support.EffectType.GutsBonus,
    },
    {
        name: 'Wit Bonus',
        effect: support.EffectType.WitBonus,
    },
    {
        name: 'Skill Point Bonus',
        effect: support.EffectType.SkillPointBonus,
    },
    {
        name: 'Training Effectiveness',
        effect: support.EffectType.TrainingEffectiveness,
    },
    {
        name: 'Initial Friendship',
        effect: support.EffectType.InitialFriendship,
    },
    {
        name: 'Race Bonus',
        effect: support.EffectType.RaceBonus,
    },
    {
        name: 'Hint Frequency',
        effect: support.EffectType.HintFrequency,
    },
    {
        name: 'Specialty Priority',
        effect: support.EffectType.SpecialtyPriority,
    },
] as const;

export type SupportCardDatum = {
    'Career': string;
    'Scenario': string;
    'Source': string;
    'Unique Effect': string;
    'Idle Mode Sub Rate': number;
    'Races': number;
    'Race Wins': number;
} & Record<typeof INDEPENDENT[number]['name'], number> & Record<typeof DEPENDENT[number]['name'], number>;

export function supportVariables(career: ism.Career, lookupCard: (id: number) => support.SupportCard, lookupScenario: (id: number) => scenario.Scenario): SupportCardDatum[] {
    const deck = career.progress_log_info.support_card_gain_info_array.map((g) => lookupCard(g.support_card_id));
    const scen = lookupScenario(career.end_info.chara_info.scenario_id);
    return career.progress_log_info.support_card_gain_info_array.map((g, i) => {
        const card = deck[i];
        const level = support.level(card.rarity, career.end_info.chara_info.support_card_array[i].exp);
        const { effects, unique } = support.effects(lookupCard(g.support_card_id), level);
        const indep = Object.fromEntries(INDEPENDENT.map((t) => [t.name, effects.find((e) => e.type === t.effect)?.value ?? 0])) as Record<typeof INDEPENDENT[number]['name'], number>;
        const dep = Object.fromEntries(DEPENDENT.map((t) => [t.name, t.map(g.gain_info, effects)])) as Record<typeof DEPENDENT[number]['name'], number>;
        return {
            'Career': label(career),
            'Scenario': scen.name,
            'Source': card.name,
            'Unique Effect': unique?.description ?? 'None',
            'Idle Mode Sub Rate': unique?.idle_mode_sub_rate ?? 0,
            'Races': career.progress_log_info.race_history_array.length,
            'Race Wins': career.progress_log_info.race_history_array.reduce((w, r) => r.race_history.result_rank === 1 ? w + 1 : w, 0),
            ...indep,
            ...dep,
        };
    });
}
