using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using System.Collections.Generic;
using System.Text;

namespace GeoSus.Client
{
    #region Základní typy

    public struct Position
    {
        [JsonProperty("lat")]
        public double Lat { get; set; }

        [JsonProperty("lon")]
        public double Lon { get; set; }

        public Position(double lat, double lon)
        {
            Lat = lat;
            Lon = lon;
        }

        // Haversine vzdálenost v metrech
        public double DistanceTo(Position other)
        {
            const double R = 6371000;
            var lat1 = Lat * Math.PI / 180;
            var lat2 = other.Lat * Math.PI / 180;
            var dLat = (other.Lat - Lat) * Math.PI / 180;
            var dLon = (other.Lon - Lon) * Math.PI / 180;

            var a = Math.Sin(dLat / 2) * Math.Sin(dLat / 2) +
                    Math.Cos(lat1) * Math.Cos(lat2) *
                    Math.Sin(dLon / 2) * Math.Sin(dLon / 2);
            var c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));

            return R * c;
        }
        public static bool operator ==(Position left, Position right) { if (left.Lat == right.Lat && left.Lon == right.Lon) { return true; } else { return false; } }
        public static bool operator !=(Position left, Position right) { return !(left == right); }
    }
[JsonConverter(typeof(StringEnumConverter))]
public enum PlayerRole { Crew, Impostor }

[JsonConverter(typeof(StringEnumConverter))]
public enum PlayerState { Alive, Dead }

[JsonConverter(typeof(StringEnumConverter))]
// NOTE: `Voting` is reserved-but-unused on the wire as of 2026. The server
// keeps the entire vote cycle inside `Meeting` and uses MeetingStartedPayload
// timestamps (DiscussionEndTime / VotingEndTime) to distinguish sub-phases.
// The enum value is preserved here for serialization compatibility with old
// saves; new code should not assign it.
public enum GamePhase { Lobby, Loading, Playing, Meeting, Voting, Ended }

[JsonConverter(typeof(StringEnumConverter))]
public enum TaskType { Instant }

[JsonConverter(typeof(StringEnumConverter))]
public enum MeetingType { BodyReport, Emergency }

[JsonConverter(typeof(StringEnumConverter))]
public enum SabotageType { CommsBlackout, CriticalMeltdown }

[JsonConverter(typeof(StringEnumConverter))]
public enum SabotageState { Inactive, Active, Repaired }

// Map data types for Overpass integration
[JsonConverter(typeof(StringEnumConverter))]
public enum PathType
{
    Footway,
    Path,
    Steps,
    Cycleway,
    Pedestrian,
    Road,
    Service,
    Residential,
    Track,
    Other
}

[JsonConverter(typeof(StringEnumConverter))]
public enum MapAreaType
{
    Park,
    Garden,
    Playground,
    Forest,
    Grass,
    Water,
    Other
}

[JsonConverter(typeof(StringEnumConverter))]
public enum MapPOIType
{
    FoodDrink,      // Restaurants, cafes, bars
    Shop,           // Shops
    Health,         // Pharmacies, hospitals
    Transport,      // Bus stops, parking
    Culture,        // Museums, theaters
    Landmark,       // Churches, monuments
    Recreation,     // Parks, playgrounds
    Other
}

#endregion

#region Zprávy

public abstract class Message
{
    [JsonProperty("type")]
    public abstract string Type { get; }
    
    [JsonProperty("clientSeq")]
    public int ClientSeq { get; set; }
    
    [JsonProperty("actionId")]
    public string? ActionId { get; set; }
}

// Handshake
public class ClientHello : Message
{
    public override string Type => "ClientHello";
    
    [JsonProperty("protocolVersion")]
    public string ProtocolVersion { get; set; } = "1.0";
    
    [JsonProperty("clientUuid")]
    public string ClientUuid { get; set; } = "";
    
    [JsonProperty("displayName")]
    public string? DisplayName { get; set; }
}

public class ServerHello : Message
{
    public override string Type => "ServerHello";
    
    [JsonProperty("rsaPublicKeyPem")]
    public string RsaPublicKeyPem { get; set; } = "";
    
    [JsonProperty("serverId")]
    public string ServerId { get; set; } = "";
}

public class KeyExchange : Message
{
    public override string Type => "KeyExchange";
    
    [JsonProperty("encryptedSessionKey")]
    public string EncryptedSessionKey { get; set; } = "";
    
    [JsonProperty("encryptedIV")]
    public string EncryptedIV { get; set; } = "";
}

public class KeyExchangeAck : Message
{
    public override string Type => "KeyExchangeAck";
    
    [JsonProperty("status")]
    public string Status { get; set; } = "";
}

// Lobby
public class CreateLobby : Message
{
    public override string Type => "CreateLobby";
    
    [JsonProperty("password")]
    public string? Password { get; set; }
    
    [JsonProperty("playAreaCenter")]
    public Position? PlayAreaCenter { get; set; }
    
    [JsonProperty("playAreaRadius")]
    public double PlayAreaRadius { get; set; } = 500;
    
    [JsonProperty("impostorCount")]
    public int ImpostorCount { get; set; } = 1;
    
    [JsonProperty("taskCount")]
    public int TaskCount { get; set; } = 5;

    /// <summary>
    /// P13b: optional per-lobby settings overrides supplied by the host.
    /// Any field left null falls through to the server's current default
    /// (snapshotted at lobby creation, immutable thereafter for this lobby).
    /// </summary>
    [JsonProperty("settings")]
    public GameSettingsOverrides? Settings { get; set; }

    /// <summary>
    /// Optional. Live host display name from the nickname input field at
    /// the moment of CreateLobby. ClientHello-time name is stale because
    /// the handshake fires before the user has typed anything; this lets
    /// the server pick up the freshly-typed name without a separate
    /// rename round-trip.
    /// </summary>
    [JsonProperty("displayName")]
    public string? DisplayName { get; set; }
}

public class CreateLobbyResponse : Message
{
    public override string Type => "CreateLobbyResponse";
    
    [JsonProperty("success")]
    public bool Success { get; set; }
    
    [JsonProperty("joinCode")]
    public string? JoinCode { get; set; }
    
    [JsonProperty("lobbyId")]
    public string? LobbyId { get; set; }
    
    [JsonProperty("error")]
    public string? Error { get; set; }
    
    [JsonProperty("lobbyState")]
    public LobbyState? LobbyState { get; set; }
}

public class JoinLobby : Message
{
    public override string Type => "JoinLobby";

    [JsonProperty("joinCode")]
    public string JoinCode { get; set; } = "";

    [JsonProperty("password")]
    public string? Password { get; set; }

    /// <summary>
    /// Optional. Live joiner display name from the nickname input field
    /// at the moment of Join. See CreateLobby.DisplayName for rationale.
    /// </summary>
    [JsonProperty("displayName")]
    public string? DisplayName { get; set; }
}

public class JoinLobbyResponse : Message
{
    public override string Type => "JoinLobbyResponse";
    
    [JsonProperty("success")]
    public bool Success { get; set; }
    
    [JsonProperty("lobbyId")]
    public string? LobbyId { get; set; }
    
    [JsonProperty("error")]
    public string? Error { get; set; }
    
    [JsonProperty("lobbyState")]
    public LobbyState? LobbyState { get; set; }
}

public class LeaveLobby : Message
{
    public override string Type => "LeaveLobby";
}

public class ReturnToLobby : Message
{
    public override string Type => "ReturnToLobby";
}

public class StartGame : Message
{
    public override string Type => "StartGame";
}

// Client confirmation that map data was received
public class MapDataReceived : Message
{
    public override string Type => "MapDataReceived";
}

// Hra
public class UpdatePosition : Message
{
    public override string Type => "UpdatePosition";
    
    [JsonProperty("position")]
    public Position Position { get; set; }
}

public class PositionBroadcast : Message
{
    public override string Type => "PositionBroadcast";
    
    [JsonProperty("players")]
    public List<PlayerPositionInfo> Players { get; set; } = new List<PlayerPositionInfo>();
}

public class PlayerPositionInfo
{
    [JsonProperty("clientUuid")]
    public string ClientUuid { get; set; } = "";
    
    [JsonProperty("position")]
    public Position Position { get; set; }
    
    [JsonProperty("state")]
    public PlayerState State { get; set; }
}

public class KillAttempt : Message
{
    public override string Type => "KillAttempt";
    
    [JsonProperty("targetClientUuid")]
    public string TargetClientUuid { get; set; } = "";
}

public class ReportBody : Message
{
    public override string Type => "ReportBody";
    
    [JsonProperty("bodyId")]
    public string BodyId { get; set; } = "";
}

public class CallEmergencyMeeting : Message
{
    public override string Type => "CallEmergencyMeeting";
}

public class CastVote : Message
{
    public override string Type => "CastVote";
    
    [JsonProperty("targetClientUuid")]
    public string? TargetClientUuid { get; set; }
}

public class TaskStart : Message
{
    public override string Type => "TaskStart";
    
    [JsonProperty("taskId")]
    public string TaskId { get; set; } = "";
}

public class TaskProgress : Message
{
    public override string Type => "TaskProgress";
    
    [JsonProperty("taskId")]
    public string TaskId { get; set; } = "";
    
    [JsonProperty("step")]
    public int Step { get; set; } = 1;
}

public class TaskComplete : Message
{
    public override string Type => "TaskComplete";
    
    [JsonProperty("taskId")]
    public string TaskId { get; set; } = "";
}

public class Ping : Message
{
    public override string Type => "Ping";
    
    [JsonProperty("clientTime")]
    public long ClientTime { get; set; }
}

public class Pong : Message
{
    public override string Type => "Pong";
    
    [JsonProperty("clientTime")]
    public long ClientTime { get; set; }
    
    [JsonProperty("serverTime")]
    public long ServerTime { get; set; }
}

public class Reconnect : Message
{
    public override string Type => "Reconnect";
    
    [JsonProperty("lobbyId")]
    public string LobbyId { get; set; } = "";
    
    [JsonProperty("lastEventId")]
    public long LastEventId { get; set; }
}

public class Ack : Message
{
    public override string Type => "Ack";
    
    [JsonProperty("ackedSeq")]
    public int AckedSeq { get; set; }
    
    [JsonProperty("success")]
    public bool Success { get; set; }
    
    [JsonProperty("error")]
    public string? Error { get; set; }
}

public class ErrorMessage : Message
{
    public override string Type => "Error";
    
    [JsonProperty("errorCode")]
    public string ErrorCode { get; set; } = "";
    
    [JsonProperty("errorText")]
    public string ErrorText { get; set; } = "";
}

// Sabotage messages
public class StartSabotage : Message
{
    public override string Type => "StartSabotage";
    
    [JsonProperty("sabotageType")]
    public SabotageType SabotageType { get; set; }
}

public class ActivateRepairStation : Message
{
    public override string Type => "ActivateRepairStation";
    
    [JsonProperty("stationId")]
    public string StationId { get; set; } = "";
}

public class DeactivateRepairStation : Message
{
    public override string Type => "DeactivateRepairStation";
    
    [JsonProperty("stationId")]
    public string StationId { get; set; } = "";
}

#endregion

#region Eventy

public class GameEvent : Message
{
    public override string Type => "GameEvent";
    
    [JsonProperty("eventId")]
    public long EventId { get; set; }
    
    [JsonProperty("serverSeq")]
    public long ServerSeq { get; set; }
    
    [JsonProperty("timestamp")]
    public DateTime Timestamp { get; set; }
    
    [JsonProperty("actor")]
    public string? Actor { get; set; }
    
    [JsonProperty("eventType")]
    public string EventType { get; set; } = "";
    
    [JsonProperty("payload")]
    public JObject? Payload { get; set; }
    
    public T? GetPayload<T>() where T : class
    {
        if (Payload == null) return null;
        return Payload.ToObject<T>(JsonSerializer.Create(JsonOptions.Default));
    }
}

// Payload typy
public class HostChangedPayload
{
    [JsonProperty("newHostId")]
    public string NewHostId { get; set; } = "";
    
    [JsonProperty("previousHostId")]
    public string PreviousHostId { get; set; } = "";
}

public class PlayerJoinedPayload
{
    [JsonProperty("clientUuid")]
    public string ClientUuid { get; set; } = "";
    
    [JsonProperty("displayName")]
    public string DisplayName { get; set; } = "";
}

public class PlayerLeftPayload
{
    [JsonProperty("clientUuid")]
    public string ClientUuid { get; set; } = "";
    
    [JsonProperty("reason")]
    public string? Reason { get; set; }
}

// Loading phase payloads
public class GameStartingPayload
{
    [JsonProperty("message")]
    public string Message { get; set; } = "";
}

public class MapDataReadyPayload
{
    [JsonProperty("mapData")]
    public MapDataPayload? MapData { get; set; }
}

public class PlayerMapDataReceivedPayload
{
    [JsonProperty("clientUuid")]
    public string ClientUuid { get; set; } = "";
    
    [JsonProperty("displayName")]
    public string DisplayName { get; set; } = "";
    
    [JsonProperty("playersReady")]
    public int PlayersReady { get; set; }
    
    [JsonProperty("totalPlayers")]
    public int TotalPlayers { get; set; }
}

public class GameStartedPayload
{
    [JsonProperty("impostorCount")]
    public int ImpostorCount { get; set; }
    
    [JsonProperty("taskCount")]
    public int TaskCount { get; set; }
}

public class RoleAssignedPayload
{
    [JsonProperty("clientUuid")]
    public string ClientUuid { get; set; } = "";
    
    [JsonProperty("role")]
    public PlayerRole Role { get; set; }
    
    [JsonProperty("tasks")]
    public List<GameTask>? Tasks { get; set; }
}

public class PlayerKilledPayload
{
    [JsonProperty("victimId")]
    public string VictimId { get; set; } = "";
    
    [JsonProperty("killerId")]
    public string KillerId { get; set; } = "";
    
    [JsonProperty("bodyId")]
    public string BodyId { get; set; } = "";
    
    [JsonProperty("location")]
    public Position Location { get; set; }
}

public class BodyReportedPayload
{
    [JsonProperty("reporterId")]
    public string ReporterId { get; set; } = "";
    
    [JsonProperty("bodyId")]
    public string BodyId { get; set; } = "";
    
    [JsonProperty("victimId")]
    public string VictimId { get; set; } = "";
}

public class EmergencyMeetingCalledPayload
{
    [JsonProperty("callerId")]
    public string CallerId { get; set; } = "";
}

public class MeetingStartedPayload
{
    [JsonProperty("meetingId")]
    public string MeetingId { get; set; } = "";
    
    [JsonProperty("type")]
    public MeetingType Type { get; set; }
    
    [JsonProperty("meetingLocation")]
    public Position MeetingLocation { get; set; }
    
    [JsonProperty("arrivalDeadline")]
    public DateTime ArrivalDeadline { get; set; }
    
    [JsonProperty("discussionEndTime")]
    public DateTime? DiscussionEndTime { get; set; }
    
    [JsonProperty("votingEndTime")]
    public DateTime VotingEndTime { get; set; }
}

public class PlayerArrivedAtMeetingPayload
{
    [JsonProperty("clientUuid")]
    public string ClientUuid { get; set; } = "";
    
    [JsonProperty("meetingId")]
    public string MeetingId { get; set; } = "";
}

public class PlayerVotedPayload
{
    [JsonProperty("voterId")]
    public string VoterId { get; set; } = "";
    
    [JsonProperty("targetId")]
    public string? TargetId { get; set; }
}

public class VotingClosedPayload
{
    [JsonProperty("voteCounts")]
    public Dictionary<string, int> VoteCounts { get; set; } = new Dictionary<string, int>();
    
    [JsonProperty("ejectedPlayerId")]
    public string? EjectedPlayerId { get; set; }
    
    [JsonProperty("wasTie")]
    public bool WasTie { get; set; }
}

public class PlayerEjectedPayload
{
    [JsonProperty("clientUuid")]
    public string ClientUuid { get; set; } = "";
    
    [JsonProperty("role")]
    public PlayerRole Role { get; set; }
}

public class TaskStartedPayload
{
    [JsonProperty("clientUuid")]
    public string ClientUuid { get; set; } = "";

    [JsonProperty("taskId")]
    public string TaskId { get; set; } = "";
}

public class TaskCompletedPayload
{
    [JsonProperty("clientUuid")]
    public string ClientUuid { get; set; } = "";

    [JsonProperty("taskId")]
    public string TaskId { get; set; } = "";

    [JsonProperty("totalCompleted")]
    public int TotalCompleted { get; set; }

    [JsonProperty("totalTasks")]
    public int TotalTasks { get; set; }
}

public class GameEndedPayload
{
    [JsonProperty("winningFaction")]
    public string WinningFaction { get; set; } = "";
    
    [JsonProperty("reason")]
    public string Reason { get; set; } = "";
    
    [JsonProperty("winners")]
    public List<string> Winners { get; set; } = new List<string>();
}

public class ReturnedToLobbyPayload
{
    [JsonProperty("message")]
    public string Message { get; set; } = "";
}

// System message payload (admin broadcast)
public class SystemMessagePayload
{
    [JsonProperty("message")]
    public string Message { get; set; } = "";
    
    [JsonProperty("timestamp")]
    public DateTime Timestamp { get; set; }
}

// Sabotage event payloads
public class SabotageStartedPayload
{
    [JsonProperty("sabotageId")]
    public string SabotageId { get; set; } = "";
    
    [JsonProperty("type")]
    public SabotageType Type { get; set; }
    
    [JsonProperty("initiatorId")]
    public string InitiatorId { get; set; } = "";
    
    [JsonProperty("deadline")]
    public DateTime? Deadline { get; set; }
    
    [JsonProperty("repairStations")]
    public List<RepairStationInfo> RepairStations { get; set; } = new List<RepairStationInfo>();
    
    [JsonProperty("requiredSimultaneousRepairs")]
    public int RequiredSimultaneousRepairs { get; set; }
}

public class RepairStationInfo
{
    [JsonProperty("stationId")]
    public string StationId { get; set; } = "";
    
    [JsonProperty("name")]
    public string Name { get; set; } = "";
    
    [JsonProperty("location")]
    public Position Location { get; set; }
    
    [JsonProperty("repairDurationMs")]
    public int RepairDurationMs { get; set; }
    
    /// <summary>
    /// Track locally if this station has been repaired
    /// </summary>
    [JsonIgnore]
    public bool IsRepaired { get; set; }
}

public class RepairStartedPayload
{
    [JsonProperty("sabotageId")]
    public string SabotageId { get; set; } = "";

    [JsonProperty("stationId")]
    public string StationId { get; set; } = "";

    [JsonProperty("playerId")]
    public string PlayerId { get; set; } = "";
}

public class RepairStoppedPayload
{
    [JsonProperty("sabotageId")]
    public string SabotageId { get; set; } = "";

    [JsonProperty("stationId")]
    public string StationId { get; set; } = "";

    [JsonProperty("playerId")]
    public string PlayerId { get; set; } = "";
}

public class SabotageRepairedPayload
{
    [JsonProperty("sabotageId")]
    public string SabotageId { get; set; } = "";
    
    [JsonProperty("type")]
    public SabotageType Type { get; set; }
    
    [JsonProperty("repairerIds")]
    public List<string> RepairerIds { get; set; } = new List<string>();
}

public class SabotageMeltdownPayload
{
    [JsonProperty("sabotageId")]
    public string SabotageId { get; set; } = "";
}

#endregion

#region State

public class LobbyState
{
    [JsonProperty("lobbyId")]
    public string LobbyId { get; set; } = "";
    
    [JsonProperty("joinCode")]
    public string JoinCode { get; set; } = "";
    
    [JsonProperty("ownerId")]
    public string? OwnerId { get; set; }
    
    [JsonProperty("phase")]
    public GamePhase Phase { get; set; }
    
    [JsonProperty("players")]
    public List<PlayerInfo> Players { get; set; } = new List<PlayerInfo>();
    
    [JsonProperty("playAreaCenter")]
    public Position PlayAreaCenter { get; set; }
    
    [JsonProperty("playAreaRadius")]
    public double PlayAreaRadius { get; set; }
    
    [JsonProperty("impostorCount")]
    public int ImpostorCount { get; set; }
    
    [JsonProperty("hasPassword")]
    public bool HasPassword { get; set; }
    
    [JsonProperty("mapData")]
    public MapDataPayload? MapData { get; set; }
    
    /// <summary>True if map data has been loaded (or Overpass is disabled)</summary>
    [JsonProperty("mapDataReady")]
    public bool MapDataReady { get; set; } = true;

    /// <summary>
    /// P13b: full per-lobby settings snapshot. Clients use this for HUD
    /// (button visibility, countdown timings, etc.) instead of hardcoded
    /// values. Always populated for new server builds; old client builds
    /// can ignore the field.
    /// </summary>
    [JsonProperty("settings")]
    public GameSettings? Settings { get; set; }
}

/// <summary>
/// P13b: per-lobby gameplay settings on the wire. Server populates this from
/// its per-lobby snapshot so clients can drive HUD logic from authoritative
/// values rather than hardcoded constants.
/// </summary>
public class GameSettings
{
    // Round shape
    [JsonProperty("maxPlayers")]
    public int MaxPlayers { get; set; }

    [JsonProperty("impostorCount")]
    public int ImpostorCount { get; set; }

    [JsonProperty("taskCount")]
    public int TaskCount { get; set; }

    [JsonProperty("tiePolicy")]
    public string TiePolicy { get; set; } = "NoEject";

    // Distances (m)
    [JsonProperty("killDistanceM")]
    public double KillDistanceM { get; set; }

    [JsonProperty("reportDistanceM")]
    public double ReportDistanceM { get; set; }

    [JsonProperty("taskStartDistanceM")]
    public double TaskStartDistanceM { get; set; }

    [JsonProperty("meetingArrivalRadiusM")]
    public double MeetingArrivalRadiusM { get; set; }

    [JsonProperty("emergencyMeetingCallRadiusM")]
    public double EmergencyMeetingCallRadiusM { get; set; }

    [JsonProperty("repairStationDistanceM")]
    public double RepairStationDistanceM { get; set; }

    // Cooldowns / counts
    [JsonProperty("killCooldownMs")]
    public int KillCooldownMs { get; set; }

    [JsonProperty("emergencyMeetingCooldownMs")]
    public int EmergencyMeetingCooldownMs { get; set; }

    [JsonProperty("maxEmergencyMeetingsPerPlayer")]
    public int MaxEmergencyMeetingsPerPlayer { get; set; }

    // Meeting phases (ms)
    [JsonProperty("arrivalBaseMs")]
    public int ArrivalBaseMs { get; set; }

    [JsonProperty("allowedLateMs")]
    public int AllowedLateMs { get; set; }

    [JsonProperty("discussionPhaseMs")]
    public int DiscussionPhaseMs { get; set; }

    [JsonProperty("votingPhaseMs")]
    public int VotingPhaseMs { get; set; }

    // Sabotage
    [JsonProperty("sabotageCooldownMs")]
    public int SabotageCooldownMs { get; set; }

    [JsonProperty("commsBlackoutDurationMs")]
    public int CommsBlackoutDurationMs { get; set; }

    [JsonProperty("criticalMeltdownDeadlineMs")]
    public int CriticalMeltdownDeadlineMs { get; set; }

    [JsonProperty("repairStationHoldMs")]
    public int RepairStationHoldMs { get; set; }
}

/// <summary>
/// P13b: host-supplied overrides at CreateLobby. Every field is nullable so
/// the host can opt into changing only what they care about; null = use the
/// server's current default at the moment of lobby creation.
/// </summary>
public class GameSettingsOverrides
{
    [JsonProperty("maxPlayers")]
    public int? MaxPlayers { get; set; }

    [JsonProperty("impostorCount")]
    public int? ImpostorCount { get; set; }

    [JsonProperty("taskCount")]
    public int? TaskCount { get; set; }

    [JsonProperty("tiePolicy")]
    public string? TiePolicy { get; set; }

    [JsonProperty("killDistanceM")]
    public double? KillDistanceM { get; set; }

    [JsonProperty("reportDistanceM")]
    public double? ReportDistanceM { get; set; }

    [JsonProperty("taskStartDistanceM")]
    public double? TaskStartDistanceM { get; set; }

    [JsonProperty("meetingArrivalRadiusM")]
    public double? MeetingArrivalRadiusM { get; set; }

    [JsonProperty("emergencyMeetingCallRadiusM")]
    public double? EmergencyMeetingCallRadiusM { get; set; }

    [JsonProperty("repairStationDistanceM")]
    public double? RepairStationDistanceM { get; set; }

    [JsonProperty("killCooldownMs")]
    public int? KillCooldownMs { get; set; }

    [JsonProperty("emergencyMeetingCooldownMs")]
    public int? EmergencyMeetingCooldownMs { get; set; }

    [JsonProperty("maxEmergencyMeetingsPerPlayer")]
    public int? MaxEmergencyMeetingsPerPlayer { get; set; }

    [JsonProperty("arrivalBaseMs")]
    public int? ArrivalBaseMs { get; set; }

    [JsonProperty("allowedLateMs")]
    public int? AllowedLateMs { get; set; }

    [JsonProperty("discussionPhaseMs")]
    public int? DiscussionPhaseMs { get; set; }

    [JsonProperty("votingPhaseMs")]
    public int? VotingPhaseMs { get; set; }

    [JsonProperty("sabotageCooldownMs")]
    public int? SabotageCooldownMs { get; set; }

    [JsonProperty("commsBlackoutDurationMs")]
    public int? CommsBlackoutDurationMs { get; set; }

    [JsonProperty("criticalMeltdownDeadlineMs")]
    public int? CriticalMeltdownDeadlineMs { get; set; }

    [JsonProperty("repairStationHoldMs")]
    public int? RepairStationHoldMs { get; set; }
}

// Map data classes for rendering - compact format from server
public class MapDataPayload
{
    [JsonProperty("center")]
    public Position Center { get; set; }
    
    [JsonProperty("radiusMeters")]
    public double RadiusMeters { get; set; }
    
    /// <summary>Buildings: [[lat,lon,lat,lon,...], ...]</summary>
    [JsonProperty("buildings")]
    public List<double[]> Buildings { get; set; } = new List<double[]>();
    
    /// <summary>Building types: ["residential", "commercial", ...]</summary>
    [JsonProperty("buildingTypes")]
    public List<string> BuildingTypes { get; set; } = new List<string>();
    
    /// <summary>Pathways: [[lat,lon,lat,lon,...], ...]</summary>
    [JsonProperty("pathways")]
    public List<double[]> Pathways { get; set; } = new List<double[]>();
    
    /// <summary>Pathway types: [0=footway, 1=steps, ...]</summary>
    [JsonProperty("pathwayTypes")]
    public List<int> PathwayTypes { get; set; } = new List<int>();
    
    /// <summary>Areas (parks): [[lat,lon,lat,lon,...], ...]</summary>
    [JsonProperty("areas")]
    public List<double[]> Areas { get; set; } = new List<double[]>();
    
    /// <summary>Area types</summary>
    [JsonProperty("areaTypes")]
    public List<int> AreaTypes { get; set; } = new List<int>();
    
    /// <summary>POIs: [lat, lon, type, lat, lon, type, ...]</summary>
    [JsonProperty("pOIs")]
    public List<double> POIs { get; set; } = new List<double>();
    
    // Helper methods for extracting structured data
    public List<MapBuilding> GetBuildings()
    {
        var result = new List<MapBuilding>();
        for (int i = 0; i < Buildings.Count; i++)
        {
            var coords = Buildings[i];
            var outline = new List<Position>();
            for (int j = 0; j < coords.Length - 1; j += 2)
            {
                outline.Add(new Position(coords[j], coords[j + 1]));
            }
            result.Add(new MapBuilding
            {
                Id = i,
                Outline = outline,
                BuildingType = i < BuildingTypes.Count ? BuildingTypes[i] : "yes"
            });
        }
        return result;
    }
    
    public List<MapPathway> GetPathways()
    {
        var result = new List<MapPathway>();
        for (int i = 0; i < Pathways.Count; i++)
        {
            var coords = Pathways[i];
            var points = new List<Position>();
            for (int j = 0; j < coords.Length - 1; j += 2)
            {
                points.Add(new Position(coords[j], coords[j + 1]));
            }
            result.Add(new MapPathway
            {
                Id = i,
                Points = points,
                PathType = i < PathwayTypes.Count ? (PathType)PathwayTypes[i] : PathType.Other
            });
        }
        return result;
    }
    
    public List<MapArea> GetAreas()
    {
        var result = new List<MapArea>();
        for (int i = 0; i < Areas.Count; i++)
        {
            var coords = Areas[i];
            var outline = new List<Position>();
            for (int j = 0; j < coords.Length - 1; j += 2)
            {
                outline.Add(new Position(coords[j], coords[j + 1]));
            }
            result.Add(new MapArea
            {
                Id = i,
                Outline = outline,
                AreaType = i < AreaTypes.Count ? (MapAreaType)AreaTypes[i] : MapAreaType.Other
            });
        }
        return result;
    }
    
    public List<MapPOI> GetPOIs()
    {
        var result = new List<MapPOI>();
        for (int i = 0; i < POIs.Count - 2; i += 3)
        {
            result.Add(new MapPOI
            {
                Id = i / 3,
                Location = new Position(POIs[i], POIs[i + 1]),
                POIType = (MapPOIType)(int)POIs[i + 2]
            });
        }
        return result;
    }
}

public class MapBuilding
{
    public long Id { get; set; }
    public List<Position> Outline { get; set; } = new List<Position>();
    public string? Name { get; set; }
    public string? BuildingType { get; set; }
}

public class MapPathway
{
    public long Id { get; set; }
    public List<Position> Points { get; set; } = new List<Position>();
    public PathType PathType { get; set; }
    public string? Name { get; set; }
}

public class MapArea
{
    public long Id { get; set; }
    public List<Position> Outline { get; set; } = new List<Position>();
    public MapAreaType AreaType { get; set; }
    public string? Name { get; set; }
}

public class MapPOI
{
    public long Id { get; set; }
    public Position Location { get; set; }
    public string? Name { get; set; }
    public MapPOIType POIType { get; set; }
}

public class PlayerInfo
{
    [JsonProperty("clientUuid")]
    public string ClientUuid { get; set; } = "";
    
    [JsonProperty("displayName")]
    public string DisplayName { get; set; } = "";
    
    [JsonProperty("isOwner")]
    public bool IsOwner { get; set; }
    
    [JsonProperty("isReady")]
    public bool IsReady { get; set; }
    
    [JsonProperty("state")]
    public PlayerState State { get; set; }
}

public class GameTask
{
    [JsonProperty("taskId")]
    public string TaskId { get; set; } = "";
    
    [JsonProperty("name")]
    public string Name { get; set; } = "";
    
    [JsonProperty("location")]
    public Position Location { get; set; }
    
    [JsonProperty("type")]
    public TaskType Type { get; set; } = TaskType.Instant;
}

public class Body
{
    [JsonProperty("bodyId")]
    public string BodyId { get; set; } = "";
    
    [JsonProperty("victimId")]
    public string VictimId { get; set; } = "";
    
    [JsonProperty("location")]
    public Position Location { get; set; }
}

#endregion

#region Serializace

public static class JsonOptions
{
    public static readonly JsonSerializerSettings Default = new JsonSerializerSettings
    {
        NullValueHandling = NullValueHandling.Ignore,
        Converters = { new StringEnumConverter() }
    };
}

public static class MessageSerializer
{
    private static readonly Dictionary<string, Type> MessageTypes = new Dictionary<string, Type>
    {
        ["ClientHello"] = typeof(ClientHello),
        ["ServerHello"] = typeof(ServerHello),
        ["KeyExchange"] = typeof(KeyExchange),
        ["KeyExchangeAck"] = typeof(KeyExchangeAck),
        ["CreateLobby"] = typeof(CreateLobby),
        ["CreateLobbyResponse"] = typeof(CreateLobbyResponse),
        ["JoinLobby"] = typeof(JoinLobby),
        ["JoinLobbyResponse"] = typeof(JoinLobbyResponse),
        ["LeaveLobby"] = typeof(LeaveLobby),
        ["ReturnToLobby"] = typeof(ReturnToLobby),
        ["StartGame"] = typeof(StartGame),
        ["MapDataReceived"] = typeof(MapDataReceived),
        ["UpdatePosition"] = typeof(UpdatePosition),
        ["PositionBroadcast"] = typeof(PositionBroadcast),
        ["KillAttempt"] = typeof(KillAttempt),
        ["ReportBody"] = typeof(ReportBody),
        ["CallEmergencyMeeting"] = typeof(CallEmergencyMeeting),
        ["CastVote"] = typeof(CastVote),
        ["TaskStart"] = typeof(TaskStart),
        ["TaskProgress"] = typeof(TaskProgress),
        ["TaskComplete"] = typeof(TaskComplete),
        ["Ping"] = typeof(Ping),
        ["Pong"] = typeof(Pong),
        ["Reconnect"] = typeof(Reconnect),
        ["Ack"] = typeof(Ack),
        ["Error"] = typeof(ErrorMessage),
        ["GameEvent"] = typeof(GameEvent)
    };
    
    public static byte[] Serialize(Message msg)
    {
        var json = JsonConvert.SerializeObject(msg, JsonOptions.Default);
        return Encoding.UTF8.GetBytes(json);
    }
    
    public static Message? Deserialize(byte[] data)
    {
        var json = Encoding.UTF8.GetString(data);
        var jObj = JObject.Parse(json);
        
        var typeName = jObj["type"]?.Value<string>();
        if (typeName == null || !MessageTypes.TryGetValue(typeName, out var type))
            return null;
            
        return (Message?)JsonConvert.DeserializeObject(json, type, JsonOptions.Default);
    }
}

    #endregion
}
