const std = @import("std");
const protocol = @import("protocol");
const Session = @import("../Session.zig");
const Packet = @import("../Packet.zig");
const Config = @import("../services/config.zig");
const Data = @import("../data.zig");
const API = @import("../api.zig");

const ArrayList = std.ArrayList;
const Allocator = std.mem.Allocator;
const CmdID = protocol.CmdID;

// function to check the list if true
fn isInList(id: u32, list: []const u32) bool {
    for (list) |item| {
        if (item == id) {
            return true;
        }
    }
    return false;
}
pub const BattleManager = struct {
    allocator: std.mem.Allocator,
    pub fn init(allocator: std.mem.Allocator) BattleManager {
        return BattleManager{ .allocator = allocator };
    }
    pub fn createBattle(self: *BattleManager) !protocol.SceneBattleInfo {
        const config = try Config.loadGameConfig(self.allocator, "config.json");
        const AllAvatars = try API.AvatarList(self.allocator, null);

        var battle = protocol.SceneBattleInfo.init(self.allocator);
        const BattleBuff = protocol.BattleBuff;

        // Avatar handler
        for (config.avatar_config.items, 0..) |avatarConf, idx| {
            var avatar = protocol.BattleAvatar.init(self.allocator);
            avatar.id = avatarConf.id;
            avatar.hp = avatarConf.hp * 100;
            avatar.sp_bar = .{ .sp_cur = avatarConf.sp * 100, .sp_max = 10000 };
            avatar.level = avatarConf.level;
            avatar.rank = avatarConf.rank;
            avatar.promotion = avatarConf.promotion;
            avatar.avatar_type = .AVATAR_FORMAL_TYPE;

            // Relics
            for (avatarConf.relics.items) |relic| {
                const r = try relicCoder(self.allocator, relic.id, relic.level, relic.main_affix_id, relic.stat1, relic.cnt1, relic.step1, relic.stat2, relic.cnt2, relic.step2, relic.stat3, relic.cnt3, relic.step3, relic.stat4, relic.cnt4, relic.step4);
                try avatar.relic_list.append(r);
            }

            // Lightcone (LC)
            const lc = protocol.BattleEquipment{
                .id = avatarConf.lightcone.id,
                .rank = avatarConf.lightcone.rank,
                .level = avatarConf.lightcone.level,
                .promotion = avatarConf.lightcone.promotion,
            };
            try avatar.equipment_list.append(lc);
            //max trace
            var talentLevel: u32 = 0;
            const skill_list: []const u32 = if (isInList(avatar.id, &Data.Rem)) &Data.skills else &Data.skills_old;
            for (skill_list) |elem| {
                talentLevel = switch (elem) {
                    1 => 6,
                    2...4 => 10,
                    301, 302 => if (isInList(avatar.id, &Data.Rem)) 6 else 1,
                    else => 1,
                };
                const talent = protocol.AvatarSkillTree{ .point_id = avatar.id * 1000 + elem, .level = talentLevel };
                try avatar.skilltree_list.append(talent);
            }
            // enable technique
            if (avatarConf.use_technique) {
                var targetIndexList = ArrayList(u32).init(self.allocator);
                try targetIndexList.append(0);
                //Add new ID when modifying for new patch

                var buffedAvatarId = avatar.id;
                if (avatar.id == 8004) {
                    buffedAvatarId = 8003;
                } else if (avatar.id == 8006) {
                    buffedAvatarId = 8005;
                } else if (avatar.id == 8008) {
                    buffedAvatarId = 8007;
                }

                for (Data.buffs_unlocked) |buffId| {
                    const idPrefix = buffId / 100;
                    if (idPrefix == buffedAvatarId) {
                        var buff = BattleBuff{
                            .id = buffId,
                            .level = 1,
                            .owner_index = @intCast(idx),
                            .wave_flag = 1,
                            .target_index_list = targetIndexList,
                            .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator),
                        };

                        try buff.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
                        try battle.buff_list.append(buff);
                    }
                }

                if (isInList(buffedAvatarId, &Data.IgnoreToughness)) {
                    var buff_tough = BattleBuff{
                        .id = 1000119, //for is_ignore toughness
                        .level = 1,
                        .owner_index = @intCast(idx),
                        .wave_flag = 1,
                        .target_index_list = targetIndexList,
                        .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator),
                    };
                    try buff_tough.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
                    try battle.buff_list.append(buff_tough);
                }

                if (buffedAvatarId == 1224) {
                    var buff_march = protocol.BattleBuff{
                        .id = 122401, //for hunt march 7th tech
                        .level = 1,
                        .owner_index = @intCast(idx),
                        .wave_flag = 1,
                        .target_index_list = targetIndexList,
                        .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator),
                    };

                    try buff_march.dynamic_values.appendSlice(&[_]protocol.BattleBuff.DynamicValuesEntry{
                        .{ .key = .{ .Const = "#ADF_1" }, .value = 3 },
                        .{ .key = .{ .Const = "#ADF_2" }, .value = 3 },
                    });
                    try battle.buff_list.append(buff_march);
                }

                if (buffedAvatarId == 1310) {
                    var buff_firefly = BattleBuff{
                        .id = 1000112, //for firefly tech
                        .level = 1,
                        .owner_index = @intCast(idx),
                        .wave_flag = 1,
                        .target_index_list = targetIndexList,
                        .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator),
                    };
                    try buff_firefly.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
                    try battle.buff_list.append(buff_firefly);
                }
            }

            try battle.pve_avatar_list.append(avatar);
        }

        // Basic battle info
        battle.battle_id = config.battle_config.battle_id;
        battle.stage_id = config.battle_config.stage_id;
        battle.logic_random_seed = @intCast(@mod(std.time.timestamp(), 0xFFFFFFFF));
        battle.rounds_limit = config.battle_config.cycle_count;
        battle.monster_wave_length = @intCast(config.battle_config.monster_wave.items.len);
        battle.world_level = 6;

        // Monster handler
        for (config.battle_config.monster_wave.items) |wave| {
            var monster_wave = protocol.SceneMonsterWave.init(self.allocator);
            monster_wave.wave_param = protocol.SceneMonsterWaveParam{ .level = config.battle_config.monster_level };
            for (wave.items) |mob_id| {
                try monster_wave.monster_list.append(.{ .monster_id = mob_id });
            }
            try battle.monster_wave_list.append(monster_wave);
        }
        // stage blessings
        for (config.battle_config.blessings.items) |blessing| {
            var targetIndexList = ArrayList(u32).init(self.allocator);
            try targetIndexList.append(0);
            var buff = protocol.BattleBuff{
                .id = blessing,
                .level = 1,
                .owner_index = 0xffffffff,
                .wave_flag = 0xffffffff,
                .target_index_list = targetIndexList,
                .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator),
            };
            try buff.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
            try battle.buff_list.append(buff);
        }
        if (isInList(1407, AllAvatars.items)) { //support Castorice
            var targetIndexList = ArrayList(u32).init(self.allocator);
            try targetIndexList.append(0);
            var mazebuff_data = BattleBuff{
                .id = 140703,
                .level = 1,
                .owner_index = 1,
                .wave_flag = @intCast(config.battle_config.monster_wave.items.len),
                .target_index_list = targetIndexList,
                .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(self.allocator),
            };
            try mazebuff_data.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
            try battle.buff_list.append(mazebuff_data);
        }

        // PF/AS scoring
        const BattleTargetInfoEntry = protocol.SceneBattleInfo.BattleTargetInfoEntry;
        battle.battle_target_info = ArrayList(BattleTargetInfoEntry).init(self.allocator);

        // target hardcode
        var pfTargetHead = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(self.allocator) };
        try pfTargetHead.battle_target_list.append(.{ .id = 10002, .progress = 0, .total_progress = 0 });
        var pfTargetTail = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(self.allocator) };
        try pfTargetTail.battle_target_list.append(.{ .id = 2001, .progress = 0, .total_progress = 0 });
        try pfTargetTail.battle_target_list.append(.{ .id = 2002, .progress = 0, .total_progress = 0 });
        var asTargetHead = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(self.allocator) };
        try asTargetHead.battle_target_list.append(.{ .id = 90005, .progress = 0, .total_progress = 0 });

        switch (battle.stage_id) {
            // PF
            30019000...30019100, 30021000...30021100, 30301000...30319000 => {
                try battle.battle_target_info.append(.{ .key = 1, .value = pfTargetHead });
                // fill blank target
                for (2..5) |i| {
                    try battle.battle_target_info.append(.{ .key = @intCast(i) });
                }
                try battle.battle_target_info.append(.{ .key = 5, .value = pfTargetTail });
            },
            // AS
            420100...420300 => {
                try battle.battle_target_info.append(.{ .key = 1, .value = asTargetHead });
            },
            else => {},
        }
        return battle;
    }
};
pub fn relicCoder(allocator: Allocator, id: u32, level: u32, main_affix_id: u32, stat1: u32, cnt1: u32, step1: u32, stat2: u32, cnt2: u32, step2: u32, stat3: u32, cnt3: u32, step3: u32, stat4: u32, cnt4: u32, step4: u32) !protocol.BattleRelic {
    var relic = protocol.BattleRelic{
        .id = id,
        .main_affix_id = main_affix_id,
        .level = level,
        .sub_affix_list = ArrayList(protocol.RelicAffix).init(allocator),
    };
    try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat1, .cnt = cnt1, .step = step1 });
    try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat2, .cnt = cnt2, .step = step2 });
    try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat3, .cnt = cnt3, .step = step3 });
    try relic.sub_affix_list.append(protocol.RelicAffix{ .affix_id = stat4, .cnt = cnt4, .step = step4 });

    return relic;
}
