using GeoSus.Client;
using System;
using System.Collections.Generic;

/// <summary>
/// Sub-phase derived client-side from the timestamps in MeetingStartedPayload.
/// Server doesn't broadcast a discrete "discussion ended" event - it embeds
/// DiscussionEndTime and VotingEndTime in the meeting-start event and gates
/// vote acceptance on those timestamps. We compute the matching client view
/// by comparing UtcNow to those values every frame.
/// </summary>
public enum MeetingSubPhase
{
    Arrival,    // before ArrivalDeadline; players are still en route to meeting point
    Discussion, // arrival deadline passed; talk only, votes server-rejected
    Voting,     // discussion ended; votes accepted until VotingEndTime
    Resolved    // VotingClosed received OR votingEndTime in the past
}

/// <summary>
/// Single source of truth for all in-game state on the client.
/// Updated exclusively by GameManager_Network; read by GameManager_UI.
/// </summary>
public class GameState
{
    // ── Phase / Role ──────────────────────────────────────────────────────────
    public GamePhase Phase         { get; set; } = GamePhase.Lobby;
    public PlayerRole? MyRole      { get; set; }
    public bool IsDead             { get; set; }

    // ── Settings (P13b) ───────────────────────────────────────────────────────
    /// <summary>
    /// Per-lobby settings snapshot from the server. Populated on
    /// LobbyJoined / LobbyCreated; immutable for the lifetime of the lobby.
    /// Null on old server builds - callers must use null-coalescing fallbacks
    /// to whatever default they previously hardcoded.
    /// </summary>
    public GameSettings Settings { get; set; }

    // ── Tasks ─────────────────────────────────────────────────────────────────
    public List<GameTask>    MyTasks             { get; set; } = new List<GameTask>();
    public HashSet<string>   MyCompletedTaskIds  { get; set; } = new HashSet<string>();
    public int               TotalCompleted      { get; set; }
    public int               TotalRequired       { get; set; }

    // ── Players ───────────────────────────────────────────────────────────────
    public List<PlayerInfo>  Players             { get; set; } = new List<PlayerInfo>();

    // ── Meeting ───────────────────────────────────────────────────────────────
    public MeetingStartedPayload  ActiveMeeting     { get; set; }
    public VotingClosedPayload    LastVoteResult    { get; set; }
    public HashSet<string>        VotedPlayerIds    { get; set; } = new HashSet<string>();
    public HashSet<string>        ArrivedPlayerIds  { get; set; } = new HashSet<string>();

    /// <summary>Per-voter latest vote target. Voter ClientUuid -> target ClientUuid, or VoteSkip for skip.</summary>
    public Dictionary<string, string> VoterTargets   { get; set; } = new Dictionary<string, string>();

    /// <summary>Live vote tallies, keyed by target ClientUuid or VoteSkip. Derived from VoterTargets.</summary>
    public Dictionary<string, int> VoteTallies       { get; set; } = new Dictionary<string, int>();

    /// <summary>Local player's latest vote target. Null = haven't voted; VoteSkip = skip; otherwise target ClientUuid.</summary>
    public string MyVoteTarget                        { get; set; }

    /// <summary>Sentinel for "skip" votes in VoterTargets / VoteTallies / MyVoteTarget.</summary>
    public const string VoteSkip = "__SKIP__";

    /// <summary>
    /// Derive the current meeting sub-phase from ActiveMeeting + LastVoteResult.
    /// Returns Arrival when no meeting is active (caller should also gate on Phase).
    /// </summary>
    public MeetingSubPhase GetMeetingSubPhase()
    {
        if (LastVoteResult != null) return MeetingSubPhase.Resolved;
        var m = ActiveMeeting;
        if (m == null) return MeetingSubPhase.Arrival;
        var now = DateTime.UtcNow;
        if (now >= m.VotingEndTime) return MeetingSubPhase.Resolved;
        if (m.DiscussionEndTime.HasValue && now < m.DiscussionEndTime.Value)
        {
            // Server enforces: arrival deadline AND discussion-end gate voting.
            // While arrival is still open we surface "Arrival" so players know
            // others may still be travelling; once arrival deadline passes we
            // surface "Discussion" until the voting window opens.
            return now < m.ArrivalDeadline ? MeetingSubPhase.Arrival : MeetingSubPhase.Discussion;
        }
        return MeetingSubPhase.Voting;
    }

    /// <summary>End-of-current-sub-phase boundary as a UTC DateTime, used for countdown rendering.</summary>
    public DateTime GetMeetingSubPhaseDeadline(MeetingSubPhase sub)
    {
        var m = ActiveMeeting;
        if (m == null) return DateTime.UtcNow;
        switch (sub)
        {
            case MeetingSubPhase.Arrival:
                return m.ArrivalDeadline;
            case MeetingSubPhase.Discussion:
                return m.DiscussionEndTime ?? m.VotingEndTime;
            case MeetingSubPhase.Voting:
                return m.VotingEndTime;
            default:
                return m.VotingEndTime;
        }
    }

    // ── Sabotage ──────────────────────────────────────────────────────────────
    public SabotageStartedPayload ActiveSabotage { get; set; }

    /// <summary>StationIds currently being repaired (server broadcasts RepairStarted/RepairStopped).</summary>
    public HashSet<string> ActiveRepairs { get; set; } = new HashSet<string>();

    // ── End game ──────────────────────────────────────────────────────────────
    public GameEndedPayload GameEndData { get; set; }

    // ── Kill cooldown (tracked by GameManager, reflected here for UI) ─────────
    public float KillCooldownRemaining { get; set; }

    // ── Notification (toast) ─────────────────────────────────────────────────
    public string ToastMessage  { get; set; }
    public float  ToastExpiry   { get; set; }   // UnityEngine.Time.time
}

