using Microsoft.AspNetCore.Mvc;
using SVSim.Database.Enums;
using SVSim.EmulatedEntrypoint.Models.Dtos;
using SVSim.Database.Repositories.Deck;
using SVSim.Database.Repositories.Globals;
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests;
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests.Common;
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests.Practice;
using SVSim.EmulatedEntrypoint.Models.Dtos.Responses.Practice;
using SVSim.EmulatedEntrypoint.Services;

namespace SVSim.EmulatedEntrypoint.Controllers;

public class PracticeController : SVSimController
{
    private readonly IDeckRepository _deckRepository;
    private readonly IGlobalsRepository _globalsRepository;
    private readonly IMissionProgressService _missionProgress;

    public PracticeController(
        IDeckRepository deckRepository,
        IGlobalsRepository globalsRepository,
        IMissionProgressService missionProgress)
    {
        _deckRepository = deckRepository;
        _globalsRepository = globalsRepository;
        _missionProgress = missionProgress;
    }

    /// <summary>
    /// /practice/info — returns the AI opponent catalog. Response data is a JSON array
    /// directly (not wrapped in an object), per spec. Backed by PracticeOpponents table,
    /// seeded by SVSim.Bootstrap from seeds/practice-opponents.json.
    /// </summary>
    [HttpPost("info")]
    public async Task<List<PracticeOpponent>> Info(BaseRequest request)
    {
        var rows = await _globalsRepository.GetPracticeOpponents();
        return rows.Select(e => new PracticeOpponent
        {
            PracticeId = e.PracticeId,
            TextId = e.TextId,
            ClassId = e.ClassId,
            CharaId = e.CharaId,
            DegreeId = e.DegreeId,
            AiDeckLevel = e.AiDeckLevel,
            AiLogicLevel = e.AiLogicLevel,
            AiMaxLife = e.AiMaxLife,
            Battle3dFieldId = e.Battle3dFieldId,
            IsMaintenance = e.IsMaintenance,
            IsCampaignPractice = e.IsCampaignPractice,
        }).ToList();
    }

    /// <summary>
    /// /practice/deck_list — returns viewer's decks scoped by format (always Format.All
    /// per spec, server can ignore the request field). Fetched via IDeckRepository so the
    /// DeckCard.Card navigation is Included; going through the heavier viewer-graph query
    /// drops that ThenInclude and ships 40 zeros instead of real card ids, which then
    /// NREs the client's SBattleLoad.InitPlayer (CardCreator returns null on id=0).
    /// </summary>
    [HttpPost("deck_list")]
    public async Task<ActionResult<PracticeDeckListResponse>> DeckList(DeckFormatRequest request)
    {
        if (!TryGetViewerId(out long viewerId)) return Unauthorized();

        var byFormat = await _deckRepository.GetDecksByFormats(viewerId, new[] { Format.Rotation, Format.Unlimited });

        return new PracticeDeckListResponse
        {
            MaintenanceCardList = new List<long>(),
            UserDeckRotation = byFormat[Format.Rotation].Select(d => new UserDeck(d)).ToList(),
            UserDeckUnlimited = byFormat[Format.Unlimited].Select(d => new UserDeck(d)).ToList(),
        };
    }

    /// <summary>
    /// /practice/start — server is essentially a no-op for practice. Spec: empty body
    /// response is fine; client tolerates missing mission_parameter.
    /// </summary>
    [HttpPost("start")]
    public Task<PracticeStartResponse> Start(BaseRequest request)
    {
        return Task.FromResult(new PracticeStartResponse());
    }

    /// <summary>
    /// /practice/finish — accept the recovery_data blob without validation; return zero
    /// XP / no rewards. Class XP bookkeeping is deferred until a per-class XP store exists.
    /// </summary>
    [HttpPost("finish")]
    public async Task<PracticeFinishResponse> Finish(PracticeFinishRequest request)
    {
        // Mission/achievement progress hook. Catalog rows for practice_win achievements use
        // opponent NAMES (e.g. "practice_win:elite:arisa") — we only have numeric class_id /
        // difficulty here, so we emit numeric forms. Bridging numeric→name to match captured
        // catalog rows is a follow-up; the infrastructure is in place.
        if (request.IsWin == 1 && TryGetViewerId(out long viewerId))
        {
            await _missionProgress.RecordEventAsync(viewerId, new[]
            {
                "practice_win",
                $"practice_win:{request.Difficulty}",
                $"practice_win:{request.Difficulty}:{request.EnemyClassId}",
            });
        }

        return new PracticeFinishResponse
        {
            GetClassExperience = 0,
            ClassExperience = 0,
            ClassLevel = 1,
            AchievedInfo = new Dictionary<string, object>(),
            RewardList = new List<Models.Dtos.Common.Reward>()
        };
    }
}
