local MonkeyGames = LibStub("AceAddon-3.0"):GetAddon("MonkeyGames")
local L = MonkeyGames.L

local NetworkManager = {}
MonkeyGames.NetworkManager = NetworkManager

local COMM_PREFIX = "HGames"
local PROTOCOL_VERSION = 1

-- Message type codes
local MSG_INVITE       = "INV"
local MSG_ACCEPT       = "ACC"
local MSG_DECLINE      = "DEC"
local MSG_MOVE         = "MOV"
local MSG_RESIGN       = "RES"
local MSG_DRAW_OFFER   = "DRO"
local MSG_DRAW_ACCEPT  = "DRA"
local MSG_DRAW_DECLINE = "DRD"
local MSG_SYNC         = "SYN"

function NetworkManager:Initialize()
    MonkeyGames:RegisterComm(COMM_PREFIX, function(prefix, message, distribution, sender)
        NetworkManager:OnMessageReceived(prefix, message, distribution, sender)
    end)
end

local function CanSendMessage()
    if C_ChatInfo and C_ChatInfo.InChatMessagingLockdown and C_ChatInfo.InChatMessagingLockdown() then
        MonkeyGames:ErrorPrint(L["Cannot send messages during chat lockdown"])
        return false
    end
    return true
end

function NetworkManager:Send(msgType, data, target)
    if not CanSendMessage() then return end

    local payload
    if data then
        payload = msgType .. ":" .. MonkeyGames:Serialize(data)
    else
        payload = msgType
    end

    MonkeyGames:SendCommMessage(COMM_PREFIX, payload, "WHISPER", target)
end

function NetworkManager:OnMessageReceived(prefix, message, distribution, sender)
    -- Strip realm name for display
    local shortName = sender:match("(.+)%-") or sender

    -- Don't process our own messages
    local myName = UnitName("player")
    if shortName == myName then return end

    -- Parse message type and optional data
    local msgType, serialized = message:match("^(%u+):?(.*)")
    if not msgType then return end

    local data = nil
    if serialized and serialized ~= "" then
        local ok, deserialized = MonkeyGames:Deserialize(serialized)
        if ok then data = deserialized end
    end

    if msgType == MSG_INVITE then
        self:HandleInvite(shortName, data)
    elseif msgType == MSG_ACCEPT then
        self:HandleAccept(shortName, data)
    elseif msgType == MSG_DECLINE then
        self:HandleDecline(shortName)
    elseif msgType == MSG_MOVE then
        self:HandleMove(shortName, data)
    elseif msgType == MSG_RESIGN then
        self:HandleResign(shortName)
    elseif msgType == MSG_DRAW_OFFER then
        self:HandleDrawOffer(shortName)
    elseif msgType == MSG_DRAW_ACCEPT then
        self:HandleDrawAccept(shortName)
    elseif msgType == MSG_DRAW_DECLINE then
        self:HandleDrawDecline(shortName)
    end
end

-- Public send methods
function NetworkManager:SendInvite(target, gameName)
    self:Send(MSG_INVITE, { game = gameName, version = PROTOCOL_VERSION }, target)
    MonkeyGames.GameManager.state = "waiting_accept"
    MonkeyGames.GameManager.opponentName = target
    MonkeyGames:ChatPrint(string.format(L["Challenge sent to %s"], target))
end

function NetworkManager:SendAccept(target, gameName, assignedColor)
    self:Send(MSG_ACCEPT, { game = gameName, color = assignedColor }, target)
end

function NetworkManager:SendDecline(target)
    self:Send(MSG_DECLINE, nil, target)
end

function NetworkManager:SendMove(move)
    local target = MonkeyGames.GameManager.opponentName
    if target then
        self:Send(MSG_MOVE, move, target)
    end
end

function NetworkManager:SendResign()
    local target = MonkeyGames.GameManager.opponentName
    if target then
        self:Send(MSG_RESIGN, nil, target)
    end
end

function NetworkManager:SendDrawOffer()
    local gm = MonkeyGames.GameManager
    if gm.state ~= "playing" or gm.gameMode ~= "multiplayer" then return end
    self:Send(MSG_DRAW_OFFER, nil, gm.opponentName)
    MonkeyGames:ChatPrint("Draw offer sent.")
end

-- Incoming message handlers
function NetworkManager:HandleInvite(sender, data)
    if not data or not data.game then return end

    StaticPopupDialogs["MONKEYGAMES_INVITE"] = {
        text = string.format(L["%s invited you to play %s"], sender, data.game),
        button1 = L["Accept"],
        button2 = L["Decline"],
        OnAccept = function()
            NetworkManager:SendAccept(sender, data.game, "black")
            MonkeyGames.GameManager:StartNewGame(data.game, "multiplayer", {
                localColor = "black",
                opponentName = sender,
            })
        end,
        OnCancel = function()
            NetworkManager:SendDecline(sender)
        end,
        timeout = 30,
        whileDead = true,
        hideOnEscape = true,
    }
    StaticPopup_Show("MONKEYGAMES_INVITE")
end

function NetworkManager:HandleAccept(sender, data)
    if not data or not data.game then return end
    MonkeyGames:ChatPrint(string.format(L["%s accepted your challenge!"], sender))
    MonkeyGames.GameManager:StartNewGame(data.game, "multiplayer", {
        localColor = "white",
        opponentName = sender,
    })
end

function NetworkManager:HandleDecline(sender)
    MonkeyGames.GameManager.state = "idle"
    MonkeyGames:ChatPrint(string.format(L["%s declined your challenge."], sender))
end

function NetworkManager:HandleMove(sender, moveData)
    if not moveData then return end
    MonkeyGames.GameManager:ReceiveMove(moveData)
end

function NetworkManager:HandleResign(sender)
    MonkeyGames:ChatPrint(string.format(L["%s resigned. You win!"], sender))
    MonkeyGames.GameManager:HandleGameOver("opponent_resigned")
end

function NetworkManager:HandleDrawOffer(sender)
    StaticPopupDialogs["MONKEYGAMES_DRAW_OFFER"] = {
        text = sender .. " " .. L["offers a draw. Accept?"],
        button1 = L["Accept"],
        button2 = L["Decline"],
        OnAccept = function()
            NetworkManager:Send(MSG_DRAW_ACCEPT, nil, sender)
            MonkeyGames.GameManager:HandleGameOver("draw_agreed")
        end,
        OnCancel = function()
            NetworkManager:Send(MSG_DRAW_DECLINE, nil, sender)
        end,
        timeout = 30,
        whileDead = true,
        hideOnEscape = true,
    }
    StaticPopup_Show("MONKEYGAMES_DRAW_OFFER")
end

function NetworkManager:HandleDrawAccept(sender)
    MonkeyGames:ChatPrint(L["Draw by agreement"])
    MonkeyGames.GameManager:HandleGameOver("draw_agreed")
end

function NetworkManager:HandleDrawDecline(sender)
    MonkeyGames:ChatPrint(sender .. " declined the draw offer.")
end
