using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SVSim.BattleNode.Bridge;
using SVSim.Database.Enums;
using SVSim.Database.Services.Friend;
using SVSim.Database.Services.Replay;
using SVSim.EmulatedEntrypoint.Constants;
using SVSim.EmulatedEntrypoint.Extensions;
using SVSim.EmulatedEntrypoint.Matching;
using SVSim.EmulatedEntrypoint.Models.Dtos.FreeBattle;
using SVSim.EmulatedEntrypoint.Models.Dtos.Requests;
using SVSim.EmulatedEntrypoint.Security.SteamSessionAuthentication;
using SVSim.EmulatedEntrypoint.Services;

namespace SVSim.EmulatedEntrypoint.Controllers;

/// <summary>
/// Free battle family — casual / unranked human PvP. No AI variant exists in the client
/// (see ApiType.cs), so the controller has no /start, no AI /finish branch, and no
/// rank-progression fields on the response. Multi-prefix URLs (rotation_free_battle/,
/// unlimited_free_battle/, free_battle/force_finish) use explicit absolute route
/// attributes per action — does not extend SVSimController's [Route("[controller]")].
///
/// Mirrors RankBattleController for now; consolidation deferred — see the design doc.
/// </summary>
[ApiController]
[Authorize(AuthenticationSchemes = SteamAuthenticationConstants.SchemeName)]
public sealed class FreeBattleController : ControllerBase
{
    private readonly IMatchingResolver _resolver;
    private readonly IMatchContextBuilder _ctxBuilder;
    private readonly IBattleContextStore _battleContextStore;
    private readonly IBattleHistoryWriter _historyWriter;
    private readonly IPlayedTogetherWriter _playedTogetherWriter;
    private readonly ILogger<FreeBattleController> _log;

    public FreeBattleController(
        IMatchingResolver resolver,
        IMatchContextBuilder ctxBuilder,
        IBattleContextStore battleContextStore,
        IBattleHistoryWriter historyWriter,
        IPlayedTogetherWriter playedTogetherWriter,
        ILogger<FreeBattleController> log)
    {
        _resolver = resolver;
        _ctxBuilder = ctxBuilder;
        _battleContextStore = battleContextStore;
        _historyWriter = historyWriter;
        _playedTogetherWriter = playedTogetherWriter;
        _log = log;
    }

    private bool TryGetViewerId(out long viewerId)
    {
        viewerId = 0;
        var claim = User.Claims.FirstOrDefault(c => c.Type == ShadowverseClaimTypes.ViewerIdClaim)?.Value;
        return claim is not null && long.TryParse(claim, out viewerId);
    }

    [HttpPost("/rotation_free_battle/do_matching")]
    public Task<IActionResult> DoMatchingRotation([FromBody] DoMatchingRequestDto req, CancellationToken ct)
        => DoMatchingInternal("rotation_free_battle", Format.Rotation, req, ct);

    [HttpPost("/unlimited_free_battle/do_matching")]
    public Task<IActionResult> DoMatchingUnlimited([FromBody] DoMatchingRequestDto req, CancellationToken ct)
        => DoMatchingInternal("unlimited_free_battle", Format.Unlimited, req, ct);

    /// <summary>
    /// Shared finish handler — FreeBattleFinishTask sends the same wire shape across
    /// all six format URLs (only rotation + unlimited are routed here in v1).
    /// </summary>
    [HttpPost("/rotation_free_battle/finish")]
    [HttpPost("/unlimited_free_battle/finish")]
    public async Task<IActionResult> Finish([FromBody] FreeBattleFinishRequestDto req, CancellationToken ct)
    {
        if (!TryGetViewerId(out var vid)) return Unauthorized();

        var ctx = _battleContextStore.TakeFor(vid);
        bool isWin = req.BattleResult == 1;

        await _historyWriter.RecordAsync(vid, ctx, isWin, ct);

        // Played-together only fires for human PvP. Free battle has no AI variant, so
        // any non-null ctx with an OpponentViewerId > 0 is a real PvP pair.
        if (ctx is { OpponentViewerId: > 0 })
        {
            await _playedTogetherWriter.RecordAsync(
                vid,
                ctx.OpponentViewerId,
                new BattleParticipationContext(
                    PlayedMode: 0,
                    // Wire battle_type: 3 = free battle (per docs/api-spec/common/types.ts.md
                    // — NetworkDefine.ServerBattleType.Free).
                    BattleType: 3,
                    DeckFormat: ctx.DeckFormat,
                    TwoPickType: ctx.TwoPickType),
                ct);
        }

        return Ok(new FreeBattleFinishResponseDto
        {
            BattleResult = req.BattleResult,
            // All other fields default to 0 (ClassLevel defaults to 1).
        });
    }

    /// <summary>
    /// Defensive no-op for the family-wide force_finish URL. Per
    /// Shadowverse_Code_2026-05-23/ApiType.cs the URL is registry-only — no client task
    /// constructs it. Returning empty {} matches the rank-battle treatment.
    /// </summary>
    [HttpPost("/free_battle/force_finish")]
    public IActionResult ForceFinish([FromBody] BaseRequest _)
    {
        if (!TryGetViewerId(out var _u)) return Unauthorized();
        return Ok(new { });
    }

    private async Task<IActionResult> DoMatchingInternal(
        string mode, Format format, DoMatchingRequestDto req, CancellationToken ct)
    {
        if (!TryGetViewerId(out var vid)) return Unauthorized();

        MatchContext ctx;
        try
        {
            // BuildForRankBattleAsync is name-misleading — it builds a generic MatchContext
            // from (viewer, format, deckNo). Rename slated for cleanup at consolidation time.
            ctx = await _ctxBuilder.BuildForRankBattleAsync(vid, format, req.DeckNo);
        }
        catch (InvalidOperationException ex)
        {
            // Most likely cause: viewer has no deck at that slot for this format. Surface
            // as 3001 RC_BATTLE_MATCHING_ILLEGAL — the client shows the standard
            // matchmaking-error dialog rather than retrying forever.
            _log.LogWarning(ex, "BuildForRankBattleAsync failed for viewer {Vid} format {Fmt} deckNo {DeckNo}; returning 3001.", vid, format, req.DeckNo);
            return Ok(new DoMatchingResponseDto { MatchingState = 3001, NodeServerUrl = "" });
        }

        var r = await _resolver.ResolveAsync(mode, new BattlePlayer(vid, ctx), ct);

        return Ok(new DoMatchingResponseDto
        {
            MatchingState = r.MatchingState,
            BattleId = r.BattleId,
            NodeServerUrl = r.NodeServerUrl,
            // Placeholder per spec — per-battle card-master split is deferred.
            CardMasterId = 0,
        });
    }
}
