using System.Text.Json.Serialization;
using ChessCubing.App.Services;

namespace ChessCubing.App.Models;

public sealed class MatchConfig
{
    [JsonPropertyName("matchLabel")]
    public string MatchLabel { get; set; } = "Rencontre ChessCubing";

    [JsonPropertyName("competitionMode")]
    public bool CompetitionMode { get; set; }

    [JsonPropertyName("mode")]
    public string Mode { get; set; } = MatchEngine.ModeTwice;

    [JsonPropertyName("preset")]
    public string Preset { get; set; } = MatchEngine.PresetFast;

    [JsonPropertyName("blockDurationMs")]
    public long BlockDurationMs { get; set; } = MatchEngine.DefaultBlockDurationMs;

    [JsonPropertyName("moveLimitMs")]
    public long MoveLimitMs { get; set; } = MatchEngine.DefaultMoveLimitMs;

    [JsonPropertyName("timeInitialMs")]
    public long TimeInitialMs { get; set; } = MatchEngine.TimeModeInitialClockMs;

    [JsonPropertyName("whiteName")]
    public string WhiteName { get; set; } = "Blanc";

    [JsonPropertyName("blackName")]
    public string BlackName { get; set; } = "Noir";

    [JsonPropertyName("arbiterName")]
    public string ArbiterName { get; set; } = string.Empty;

    [JsonPropertyName("eventName")]
    public string EventName { get; set; } = string.Empty;

    [JsonPropertyName("notes")]
    public string Notes { get; set; } = string.Empty;
}

public sealed class MatchState
{
    [JsonPropertyName("schemaVersion")]
    public int SchemaVersion { get; set; } = 3;

    [JsonPropertyName("config")]
    public MatchConfig Config { get; set; } = new();

    [JsonPropertyName("phase")]
    public string Phase { get; set; } = MatchEngine.PhaseBlock;

    [JsonPropertyName("running")]
    public bool Running { get; set; }

    [JsonPropertyName("lastTickAt")]
    public long? LastTickAt { get; set; }

    [JsonPropertyName("blockNumber")]
    public int BlockNumber { get; set; } = 1;

    [JsonPropertyName("currentTurn")]
    public string CurrentTurn { get; set; } = MatchEngine.ColorWhite;

    [JsonPropertyName("blockRemainingMs")]
    public long BlockRemainingMs { get; set; } = MatchEngine.DefaultBlockDurationMs;

    [JsonPropertyName("moveRemainingMs")]
    public long MoveRemainingMs { get; set; } = MatchEngine.DefaultMoveLimitMs;

    [JsonPropertyName("quota")]
    public int Quota { get; set; } = 6;

    [JsonPropertyName("moves")]
    public PlayerIntPair Moves { get; set; } = new();

    [JsonPropertyName("clocks")]
    public PlayerLongPair? Clocks { get; set; }

    [JsonPropertyName("lastMover")]
    public string? LastMover { get; set; }

    [JsonPropertyName("awaitingBlockClosure")]
    public bool AwaitingBlockClosure { get; set; }

    [JsonPropertyName("closureReason")]
    public string ClosureReason { get; set; } = string.Empty;

    [JsonPropertyName("result")]
    public string? Result { get; set; }

    [JsonPropertyName("cube")]
    public CubeState Cube { get; set; } = new();

    [JsonPropertyName("doubleCoup")]
    public DoubleCoupState DoubleCoup { get; set; } = new();

    [JsonPropertyName("history")]
    public List<MatchHistoryEntry> History { get; set; } = [];
}

public sealed class PlayerIntPair
{
    [JsonPropertyName("white")]
    public int White { get; set; }

    [JsonPropertyName("black")]
    public int Black { get; set; }
}

public sealed class PlayerLongPair
{
    [JsonPropertyName("white")]
    public long White { get; set; }

    [JsonPropertyName("black")]
    public long Black { get; set; }
}

public sealed class PlayerNullableLongPair
{
    [JsonPropertyName("white")]
    public long? White { get; set; }

    [JsonPropertyName("black")]
    public long? Black { get; set; }
}

public sealed class CubeState
{
    [JsonPropertyName("number")]
    public int? Number { get; set; }

    [JsonPropertyName("running")]
    public bool Running { get; set; }

    [JsonPropertyName("startedAt")]
    public long? StartedAt { get; set; }

    [JsonPropertyName("elapsedMs")]
    public long ElapsedMs { get; set; }

    [JsonPropertyName("phaseAlertPending")]
    public bool PhaseAlertPending { get; set; }

    [JsonPropertyName("times")]
    public PlayerNullableLongPair Times { get; set; } = new();

    [JsonPropertyName("playerState")]
    public CubePlayerStates PlayerState { get; set; } = new();

    [JsonPropertyName("round")]
    public int Round { get; set; } = 1;

    [JsonPropertyName("history")]
    public List<CubeHistoryEntry> History { get; set; } = [];
}

public sealed class CubePlayerStates
{
    [JsonPropertyName("white")]
    public CubePlayerState White { get; set; } = new();

    [JsonPropertyName("black")]
    public CubePlayerState Black { get; set; } = new();
}

public sealed class CubePlayerState
{
    [JsonPropertyName("running")]
    public bool Running { get; set; }

    [JsonPropertyName("startedAt")]
    public long? StartedAt { get; set; }

    [JsonPropertyName("elapsedMs")]
    public long ElapsedMs { get; set; }
}

public sealed class DoubleCoupState
{
    [JsonPropertyName("eligible")]
    public bool Eligible { get; set; }

    [JsonPropertyName("step")]
    public int Step { get; set; }

    [JsonPropertyName("starter")]
    public string Starter { get; set; } = MatchEngine.ColorWhite;
}

public sealed class MatchHistoryEntry
{
    [JsonPropertyName("message")]
    public string Message { get; set; } = string.Empty;

    [JsonPropertyName("time")]
    public string Time { get; set; } = string.Empty;
}

public sealed class CubeHistoryEntry
{
    [JsonPropertyName("blockNumber")]
    public int BlockNumber { get; set; }

    [JsonPropertyName("number")]
    public int? Number { get; set; }

    [JsonPropertyName("white")]
    public long? White { get; set; }

    [JsonPropertyName("black")]
    public long? Black { get; set; }
}

public sealed class SetupFormModel
{
    public bool CompetitionMode { get; set; }
    public string MatchLabel { get; set; } = string.Empty;
    public string Mode { get; set; } = MatchEngine.ModeTwice;
    public string Preset { get; set; } = MatchEngine.PresetFast;
    public int BlockMinutes { get; set; } = 3;
    public int MoveSeconds { get; set; } = 20;
    public int TimeInitialMinutes { get; set; } = 10;
    public string WhiteName { get; set; } = "Blanc";
    public string BlackName { get; set; } = "Noir";
    public string ArbiterName { get; set; } = string.Empty;
    public string EventName { get; set; } = string.Empty;
    public string Notes { get; set; } = string.Empty;

    public MatchConfig ToMatchConfig()
    {
        return new MatchConfig
        {
            MatchLabel = MatchEngine.SanitizeText(MatchLabel) is { Length: > 0 } label ? label : "Rencontre ChessCubing",
            CompetitionMode = CompetitionMode,
            Mode = Mode,
            Preset = Preset,
            BlockDurationMs = MatchEngine.NormalizeDurationMs(BlockMinutes * 60_000L, MatchEngine.DefaultBlockDurationMs),
            MoveLimitMs = MatchEngine.NormalizeDurationMs(MoveSeconds * 1_000L, MatchEngine.DefaultMoveLimitMs),
            TimeInitialMs = MatchEngine.NormalizeDurationMs(TimeInitialMinutes * 60_000L, MatchEngine.TimeModeInitialClockMs),
            WhiteName = MatchEngine.SanitizeText(WhiteName) is { Length: > 0 } white ? white : "Blanc",
            BlackName = MatchEngine.SanitizeText(BlackName) is { Length: > 0 } black ? black : "Noir",
            ArbiterName = MatchEngine.SanitizeText(ArbiterName),
            EventName = MatchEngine.SanitizeText(EventName),
            Notes = MatchEngine.SanitizeText(Notes),
        };
    }

    public static SetupFormModel CreateDemo()
    {
        return new SetupFormModel
        {
            CompetitionMode = true,
            MatchLabel = "Demo officielle ChessCubing",
            Mode = MatchEngine.ModeTwice,
            Preset = MatchEngine.PresetFreeze,
            BlockMinutes = 3,
            MoveSeconds = 20,
            TimeInitialMinutes = 10,
            WhiteName = "Nora",
            BlackName = "Leo",
            ArbiterName = "Arbitre demo",
            EventName = "Session telephone",
            Notes = "8 cubes verifies, variante prete, tirage au sort effectue.",
        };
    }
}

public sealed record MatchPresetInfo(string Label, int Quota, string Description);

public sealed record MatchModeInfo(string Label, string Subtitle);

public sealed record TimeAdjustmentPreview(
    string BlockType,
    string? Winner,
    long CappedWhite,
    long CappedBlack,
    long WhiteDelta,
    long BlackDelta,
    long WhiteAfter,
    long BlackAfter);
