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 ChallengeData = @import("../services/challenge.zig");
const NodeCheck = @import("../commands/value.zig");

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

pub var selectedAvatarID = [_]u32{ 1304, 1313, 1406, 1004 };

// Function to check if an ID is in a list
fn isInList(id: u32, list: []const u32) bool {
    for (list) |item| {
        if (item == id) {
            return true;
        }
    }
    return false;
}
fn createBattleRelic(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;
}
fn createBattleAvatar(allocator: Allocator, avatarConf: Config.Avatar) !protocol.BattleAvatar {
    var avatar = protocol.BattleAvatar.init(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;
    if (isInList(avatar.id, &Data.EnhanceAvatarID)) avatar.GLACKHOPMIO = 1;

    // Relics
    for (avatarConf.relics.items) |relic| {
        const r = try createBattleRelic(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
    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);

    // Skills
    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,
        };
        var point_id: u32 = 0;
        if (isInList(avatar.id, &Data.EnhanceAvatarID)) point_id = avatar.id + 10000 else point_id = avatar.id;
        const talent = protocol.AvatarSkillTree{ .point_id = point_id * 1000 + elem, .level = talentLevel };
        try avatar.skilltree_list.append(talent);
    }
    return avatar;
}

// Function to add technique buffs
fn addTechniqueBuffs(allocator: Allocator, battle: *protocol.SceneBattleInfo, avatar: protocol.BattleAvatar, avatarConf: Config.Avatar, avatar_index: u32) !void {
    if (!avatarConf.use_technique) return;

    var targetIndexList = ArrayList(u32).init(allocator);
    try targetIndexList.append(0);

    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 = protocol.BattleBuff{
                .id = buffId,
                .level = 1,
                .owner_index = @intCast(avatar_index),
                .wave_flag = 1,
                .target_index_list = targetIndexList,
                .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(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 = protocol.BattleBuff{
            .id = 1000119, //for is_ignore toughness
            .level = 1,
            .owner_index = @intCast(avatar_index),
            .wave_flag = 1,
            .target_index_list = targetIndexList,
            .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(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(avatar_index),
            .wave_flag = 1,
            .target_index_list = targetIndexList,
            .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(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 = protocol.BattleBuff{
            .id = 1000112, //for firefly tech
            .level = 1,
            .owner_index = @intCast(avatar_index),
            .wave_flag = 1,
            .target_index_list = targetIndexList,
            .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator),
        };
        try buff_firefly.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
        try battle.buff_list.append(buff_firefly);
    }

    if (buffedAvatarId == 8007) {
        var buff_rmc = protocol.BattleBuff{
            .id = 800701, //for rmc tech
            .level = 1,
            .owner_index = @intCast(avatar_index),
            .wave_flag = 1,
            .target_index_list = targetIndexList,
            .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator),
        };
        try buff_rmc.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
        try battle.buff_list.append(buff_rmc);
    }
}

// Function to add future global buff.
fn addGolbalPassive(allocator: Allocator, battle: *protocol.SceneBattleInfo, wave_flag: u32) !void {
    if (isInList(1407, Data.AllAvatars)) { //support Castorice
        var targetIndexList = ArrayList(u32).init(allocator);
        try targetIndexList.append(0);
        var mazebuff_data = protocol.BattleBuff{
            .id = 140703,
            .level = 1,
            .owner_index = 1,
            .wave_flag = wave_flag,
            .target_index_list = targetIndexList,
            .dynamic_values = ArrayList(protocol.BattleBuff.DynamicValuesEntry).init(allocator),
        };
        try mazebuff_data.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
        try battle.buff_list.append(mazebuff_data);
    }
}
fn createBattleInfo(allocator: Allocator, config: Config.GameConfig, stage_monster_wave_len: u32, stage_id: u32, rounds_limit: u32) protocol.SceneBattleInfo {
    var battle = protocol.SceneBattleInfo.init(allocator);
    battle.battle_id = config.battle_config.battle_id;
    battle.stage_id = stage_id;
    battle.logic_random_seed = @intCast(@mod(std.time.timestamp(), 0xFFFFFFFF));
    battle.rounds_limit = rounds_limit;
    battle.monster_wave_length = @intCast(stage_monster_wave_len);
    battle.world_level = 6;
    return battle;
}
fn addMonsterWaves(allocator: Allocator, battle: *protocol.SceneBattleInfo, monster_wave_configs: std.ArrayList(std.ArrayList(u32)), monster_level: u32) !void { // Added monster_level
    for (monster_wave_configs.items) |wave| {
        var monster_wave = protocol.SceneMonsterWave.init(allocator);
        monster_wave.wave_param = protocol.SceneMonsterWaveParam{ .level = 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);
    }
}
fn addStageBlessings(allocator: Allocator, battle: *protocol.SceneBattleInfo, blessings: []const u32) !void {
    for (blessings) |blessing| {
        var targetIndexList = ArrayList(u32).init(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(allocator),
        };
        try buff.dynamic_values.append(.{ .key = .{ .Const = "SkillIndex" }, .value = 0 });
        try battle.buff_list.append(buff);
    }
}
fn addBattleTargets(allocator: Allocator, battle: *protocol.SceneBattleInfo) !void {
    // PF/AS scoring
    battle.battle_target_info = ArrayList(protocol.SceneBattleInfo.BattleTargetInfoEntry).init(allocator);

    // target hardcode
    var pfTargetHead = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(allocator) };
    if (ChallengeData.on_challenge == true) {
        if (NodeCheck.challenge_node == 0) {
            try pfTargetHead.battle_target_list.append(.{ .id = 10003, .progress = 0, .total_progress = 80000 });
        } else {
            try pfTargetHead.battle_target_list.append(.{ .id = 10003, .progress = 40000, .total_progress = 80000 });
        }
    } else {
        try pfTargetHead.battle_target_list.append(.{ .id = 10002, .progress = 0, .total_progress = 80000 });
    }
    var pfTargetTail = protocol.BattleTargetList{ .battle_target_list = ArrayList(protocol.BattleTarget).init(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(allocator) };
    try asTargetHead.battle_target_list.append(.{ .id = 90005, .progress = 2000, .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..4) |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 => {},
    }
}
pub const BattleManager = struct {
    allocator: Allocator,

    pub fn init(allocator: Allocator) BattleManager {
        return BattleManager{ .allocator = allocator };
    }

    pub fn createBattle(self: *BattleManager) !protocol.SceneBattleInfo {
        const config = try Config.loadGameConfig(self.allocator, "config.json");
        var battle = createBattleInfo(self.allocator, config, @intCast(config.battle_config.monster_wave.items.len), config.battle_config.stage_id, config.battle_config.cycle_count);
        var avatarIndex: u32 = 0;
        var initial_mode = false;
        while (true) {
            if (!initial_mode) {
                for (selectedAvatarID) |selected_id| {
                    for (config.avatar_config.items) |avatarConf| {
                        if (avatarConf.id == selected_id) {
                            const avatar = try createBattleAvatar(self.allocator, avatarConf);
                            try addTechniqueBuffs(self.allocator, &battle, avatar, avatarConf, avatarIndex);
                            try battle.pve_avatar_list.append(avatar);
                            avatarIndex += 1;
                            break;
                        }
                    }
                    if (avatarIndex >= 4) break;
                }
            }
            if (avatarIndex == 0 and !initial_mode) {
                initial_mode = true;
                continue;
            }
            break;
        }
        try addMonsterWaves(self.allocator, &battle, config.battle_config.monster_wave, config.battle_config.monster_level);
        try addStageBlessings(self.allocator, &battle, config.battle_config.blessings.items);
        try addGolbalPassive(self.allocator, &battle, @intCast(config.battle_config.monster_wave.items.len));
        try addBattleTargets(self.allocator, &battle);
        return battle;
    }
};

pub const ChallegeStageManager = struct {
    allocator: Allocator,

    pub fn init(allocator: Allocator) ChallegeStageManager {
        return ChallegeStageManager{ .allocator = allocator };
    }

    pub fn createChallegeStage(self: *ChallegeStageManager) !protocol.SceneBattleInfo {
        const config = try Config.loadGameConfig(self.allocator, "config.json");
        const stage = try Config.loadStageConfig(self.allocator, "resources/StageConfig.json");
        var battle: protocol.SceneBattleInfo = undefined;
        for (stage.stage_config.items) |stageConf| {
            if (stageConf.stage_id == ChallengeData.challenge_stageID) {
                battle = createBattleInfo(self.allocator, config, @intCast(stageConf.monster_list.items.len), stageConf.stage_id, if (ChallengeData.challenge_mode != 1) 30 else 4);
                var avatarIndex: u32 = 0;
                var initial_mode = false;
                while (true) {
                    if (!initial_mode) {
                        for (selectedAvatarID) |selected_id| {
                            for (config.avatar_config.items) |avatarConf| {
                                if (avatarConf.id == selected_id) {
                                    const avatar = try createBattleAvatar(self.allocator, avatarConf);
                                    try addTechniqueBuffs(self.allocator, &battle, avatar, avatarConf, avatarIndex);
                                    try battle.pve_avatar_list.append(avatar);
                                    avatarIndex += 1;
                                    break;
                                }
                            }
                            if (avatarIndex >= 4) break;
                        }
                    }
                    if (avatarIndex == 0 and !initial_mode) {
                        initial_mode = true;
                        continue;
                    }
                    break;
                }
                try addMonsterWaves(self.allocator, &battle, stageConf.monster_list, stageConf.level);
                try addStageBlessings(self.allocator, &battle, ChallengeData.challenge_blessing);
                try addGolbalPassive(self.allocator, &battle, @intCast(stageConf.monster_list.items.len));
                try addBattleTargets(self.allocator, &battle);
                break;
            }
        }
        return battle;
    }
};
