using EggLink.DanhengServer.Data;
using EggLink.DanhengServer.Database;
using EggLink.DanhengServer.Database.Quests;
using EggLink.DanhengServer.Enums.Mission;
using EggLink.DanhengServer.GameServer.Game.Player;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lineup;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.StoryLine;
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Util;

namespace EggLink.DanhengServer.GameServer.Game.Mission;

public class StoryLineManager(PlayerInstance player) : BasePlayerManager(player)
{
    public StoryLineData StoryLineData { get; set; } =
        DatabaseHelper.Instance!.GetInstanceOrCreateNew<StoryLineData>(player.Uid);

    public async ValueTask CheckIfEnterStoryLine()
    {
        if (StoryLineData.CurStoryLineId != 0) return;

        foreach (var storyLine in GameData.StoryLineData.Values)
            if (Player.MissionManager!.GetSubMissionStatus(storyLine.BeginCondition.Param) == MissionPhaseEnum.Finish)
            {
                await InitStoryLine(storyLine.StoryLineID);
                return;
            }
    }

    public async ValueTask InitStoryLine(int storyLineId, int entryId = 0, int anchorGroupId = 0, int anchorId = 0)
    {
        if (StoryLineData.CurStoryLineId != 0) await FinishStoryLine(entryId, anchorGroupId, anchorId, false);
        GameData.StoryLineData.TryGetValue(storyLineId, out var storyExcel);
        if (storyExcel == null) return;
        StoryLineData.RunningStoryLines.TryGetValue(storyLineId, out var lineInfo);
        StoryLineData.OldEntryId = Player.Data.EntryId;
        StoryLineData.OldFloorId = Player.Data.FloorId;
        StoryLineData.OldPlaneId = Player.Data.PlaneId;
        StoryLineData.OldPos = Player.Data.Pos!;
        StoryLineData.OldRot = Player.Data.Rot!;

        var avatarList = Player.LineupManager!.GetCurLineup()!.BaseAvatars!
            .Select(x => x.SpecialAvatarId > 0 ? x.SpecialAvatarId / 10 : x.BaseAvatarId).ToList();

        Player.LineupManager!.SetExtraLineup(ExtraLineupType.LineupHeliobus, avatarList);

        StoryLineData.CurStoryLineId = storyExcel.StoryLineID;
        await Player.SendPacket(new PacketStoryLineInfoScNotify(Player));
        await Player.SendPacket(new PacketSyncLineupNotify(Player.LineupManager!.GetCurLineup()!));
        if (entryId > 0)
        {
            await Player.EnterSceneByEntranceId(entryId, anchorGroupId, anchorId, true);
        }
        else
        {
            if (lineInfo == null)
                await Player.EnterSceneByEntranceId(storyExcel.InitEntranceID, storyExcel.InitGroupID,
                    storyExcel.InitAnchorID,
                    true);
            else
                await Player.LoadScene(lineInfo.SavedPlaneId, lineInfo.SavedFloorId, lineInfo.SavedEntryId,
                    lineInfo.SavedPos, lineInfo.SavedRot, true);
        }

        await Player.SendPacket(
            new PacketChangeStoryLineFinishScNotify(storyExcel.StoryLineID, ChangeStoryLineAction.FinishAction));


        var record = new StoryLineInfo
        {
            Lineup = Player.LineupManager!.GetCurLineup()!.BaseAvatars!,
            SavedEntryId = Player.Data.EntryId,
            SavedFloorId = Player.Data.FloorId,
            SavedPlaneId = Player.Data.PlaneId,
            SavedPos = Player.Data.Pos!,
            SavedRot = Player.Data.Rot!,
            StoryLineId = storyExcel.StoryLineID
        };

        StoryLineData.RunningStoryLines[storyExcel.StoryLineID] = record;
    }

    public async ValueTask EnterStoryLine(int storyLineId, bool tp = true)
    {
        if (StoryLineData.CurStoryLineId == storyLineId) return; // already in this story line

        if (storyLineId == 0) // leave story line
        {
            await LeaveStoryLine(tp);
            return;
        }

        StoryLineData.RunningStoryLines.TryGetValue(storyLineId, out var lineInfo);
        if (lineInfo == null) return;

        if (StoryLineData.CurStoryLineId == 0) // not in any story line
        {
            StoryLineData.OldEntryId = Player.Data.EntryId;
            StoryLineData.OldFloorId = Player.Data.FloorId;
            StoryLineData.OldPlaneId = Player.Data.PlaneId;
            StoryLineData.OldPos = Player.Data.Pos!;
            StoryLineData.OldRot = Player.Data.Rot!;
        }
        else // in another story line
        {
            await LeaveStoryLine(false);
        }

        Player.LineupManager!.SetExtraLineup(ExtraLineupType.LineupHeliobus,
            lineInfo.Lineup.Select(x => x.SpecialAvatarId > 0 ? x.SpecialAvatarId : x.BaseAvatarId).ToList());

        StoryLineData.CurStoryLineId = lineInfo.StoryLineId;
        await Player.SendPacket(new PacketStoryLineInfoScNotify(Player));
        await Player.SendPacket(new PacketSyncLineupNotify(Player.LineupManager!.GetCurLineup()!));

        if (tp)
            await Player.LoadScene(lineInfo.SavedPlaneId, lineInfo.SavedFloorId, lineInfo.SavedEntryId,
                lineInfo.SavedPos, lineInfo.SavedRot, true);
        await Player.SendPacket(
            new PacketChangeStoryLineFinishScNotify(StoryLineData.CurStoryLineId, ChangeStoryLineAction.Client));
    }

    public async ValueTask LeaveStoryLine(bool tp)
    {
        if (StoryLineData.CurStoryLineId == 0) return;

        GameData.StoryLineData.TryGetValue(StoryLineData.CurStoryLineId, out var storyExcel);
        if (storyExcel == null) return;

        var record = new StoryLineInfo
        {
            Lineup = Player.LineupManager!.GetCurLineup()!.BaseAvatars!,
            SavedEntryId = Player.Data.EntryId,
            SavedFloorId = Player.Data.FloorId,
            SavedPlaneId = Player.Data.PlaneId,
            SavedPos = Player.Data.Pos!,
            SavedRot = Player.Data.Rot!,
            StoryLineId = storyExcel.StoryLineID
        };

        // reset
        Player.LineupManager!.SetExtraLineup(ExtraLineupType.LineupNone, []);

        // save
        StoryLineData.RunningStoryLines[storyExcel.StoryLineID] = record;
        StoryLineData.CurStoryLineId = 0;

        await Player.SendPacket(new PacketStoryLineInfoScNotify(Player));
        await Player.SendPacket(new PacketSyncLineupNotify(Player.LineupManager!.GetCurLineup()!));

        if (tp)
        {
            await Player.LoadScene(StoryLineData.OldPlaneId, StoryLineData.OldFloorId, StoryLineData.OldEntryId,
                StoryLineData.OldPos, StoryLineData.OldRot, true);

            StoryLineData.OldPlaneId = 0;
            StoryLineData.OldEntryId = 0;
            StoryLineData.OldFloorId = 0;
            StoryLineData.OldPos = new Position();
            StoryLineData.OldRot = new Position();
        }

        await Player.SendPacket(new PacketChangeStoryLineFinishScNotify(0, ChangeStoryLineAction.None));
    }

    public async ValueTask
        CheckIfFinishStoryLine() // seems like a story line end with another ChangeStoryLine finish action that Params[0] = 0
    {
        if (StoryLineData.CurStoryLineId == 0) return;
        GameData.StoryLineData.TryGetValue(StoryLineData.CurStoryLineId, out var storyExcel);
        if (storyExcel == null) return;

        if (Player.MissionManager!.GetSubMissionStatus(storyExcel.EndCondition.Param) == MissionPhaseEnum.Finish)
            await FinishStoryLine();
    }

    public async ValueTask FinishStoryLine(int entryId = 0, int anchorGroupId = 0, int anchorId = 0, bool tp = true)
    {
        if (StoryLineData.CurStoryLineId == 0) return;

        GameData.StoryLineData.TryGetValue(StoryLineData.CurStoryLineId, out var storyExcel);
        if (storyExcel == null) return;
        Player.LineupManager!.SetExtraLineup(ExtraLineupType.LineupNone, []);

        // delete old & reset
        if (Player.MissionManager!.GetSubMissionStatus(storyExcel.EndCondition.Param) == MissionPhaseEnum.Finish)
            StoryLineData.RunningStoryLines.Remove(StoryLineData.CurStoryLineId);
        else
            StoryLineData.RunningStoryLines[StoryLineData.CurStoryLineId] = new StoryLineInfo
            {
                Lineup = Player.LineupManager!.GetCurLineup()!.BaseAvatars!,
                SavedEntryId = Player.Data.EntryId,
                SavedFloorId = Player.Data.FloorId,
                SavedPlaneId = Player.Data.PlaneId,
                SavedPos = Player.Data.Pos!,
                SavedRot = Player.Data.Rot!,
                StoryLineId = StoryLineData.CurStoryLineId
            };
        StoryLineData.CurStoryLineId = 0;

        StoryLineData.OldPlaneId = 0;
        StoryLineData.OldEntryId = 0;
        StoryLineData.OldFloorId = 0;
        StoryLineData.OldPos = new Position();
        StoryLineData.OldRot = new Position();

        await Player.SendPacket(new PacketSyncLineupNotify(Player.LineupManager!.GetCurLineup()!));
        await Player.SendPacket(new PacketStoryLineInfoScNotify(Player));
        await Player.SendPacket(new PacketChangeStoryLineFinishScNotify(0, ChangeStoryLineAction.None));

        if (tp)
        {
            if (entryId > 0)
                await Player.EnterSceneByEntranceId(entryId, anchorGroupId, anchorId, true);
            else
                await Player.LoadScene(StoryLineData.OldPlaneId, StoryLineData.OldFloorId, StoryLineData.OldEntryId,
                    StoryLineData.OldPos, StoryLineData.OldRot, true);
        }
    }

    public async ValueTask OnLogin()
    {
        await Player.SendPacket(new PacketStoryLineInfoScNotify(Player));
    }
}