local http = minetest.request_http_api()
local settings = minetest.settings

-- Configuration
local port = settings:get('mcp_chat.port') or 8081
local relay_chat = settings:get_bool('mcp_chat.relay_chat', true)
local allow_commands = settings:get_bool('mcp_chat.allow_commands', true)
local chat_prefix = settings:get('mcp_chat.chat_prefix') or '[MCP]'
local text_color = settings:get('mcp_chat.text_color') or '#00ff00'
local timeout = tonumber(settings:get('mcp_chat.timeout') or 10)
local poll_interval = tonumber(settings:get('mcp_chat.poll_interval') or 0.2)

-- Chat history buffer
local chat_history = {}
local max_history = 50

-- Initialize mod
mcp_chat = {}

-- Virtual player name for WorldEdit operations
local VIRTUAL_PLAYER = "Claude"

-- Storage for persistent data
local storage = minetest.get_mod_storage()

-- Save WorldEdit positions for virtual player
local function save_virtual_player_positions()
    if worldedit then
        local pos1 = worldedit.pos1 and worldedit.pos1[VIRTUAL_PLAYER]
        local pos2 = worldedit.pos2 and worldedit.pos2[VIRTUAL_PLAYER]
        
        if pos1 then
            storage:set_string("worldedit_pos1", minetest.serialize(pos1))
        else
            storage:set_string("worldedit_pos1", "")
        end
        
        if pos2 then
            storage:set_string("worldedit_pos2", minetest.serialize(pos2))
        else
            storage:set_string("worldedit_pos2", "")
        end
    end
end

-- Handle WorldEdit commands that fail due to player object issues
local function handle_worldedit_fallback(cmd_name, params, player_name)
    if not worldedit then
        return false, "WorldEdit not available"
    end
    
    -- Remove leading slash from command name
    local we_cmd = cmd_name:sub(2)
    
    -- Handle position setting commands
    if we_cmd == "pos1" or we_cmd == "1" then
        -- Parse coordinates if provided
        if params and params ~= "" then
            local x, y, z = params:match("^%s*([%-%.%d]+)[%s,]+([%-%.%d]+)[%s,]+([%-%.%d]+)%s*$")
            if x and y and z then
                worldedit.pos1[player_name] = {x = tonumber(x), y = tonumber(y), z = tonumber(z)}
            else
                return false, "Invalid coordinates format. Use: X Y Z or X,Y,Z"
            end
        else
            -- No coordinates provided - would need player position
            return false, "Virtual player cannot use current position. Specify coordinates: //pos1 X Y Z"
        end
        
        local pos = worldedit.pos1[player_name]
        if worldedit.mark_pos1 then
            worldedit.mark_pos1(player_name)
        end
        -- Save positions after setting
        if player_name == VIRTUAL_PLAYER then
            save_virtual_player_positions()
        end
        return true, string.format("position 1 set to (%d, %d, %d)", pos.x, pos.y, pos.z)
        
    elseif we_cmd == "pos2" or we_cmd == "2" then
        -- Parse coordinates if provided
        if params and params ~= "" then
            local x, y, z = params:match("^%s*([%-%.%d]+)[%s,]+([%-%.%d]+)[%s,]+([%-%.%d]+)%s*$")
            if x and y and z then
                worldedit.pos2[player_name] = {x = tonumber(x), y = tonumber(y), z = tonumber(z)}
            else
                return false, "Invalid coordinates format. Use: X Y Z or X,Y,Z"
            end
        else
            -- No coordinates provided - would need player position
            return false, "Virtual player cannot use current position. Specify coordinates: //pos2 X Y Z"
        end
        
        local pos = worldedit.pos2[player_name]
        if worldedit.mark_pos2 then
            worldedit.mark_pos2(player_name)
        end
        -- Save positions after setting
        if player_name == VIRTUAL_PLAYER then
            save_virtual_player_positions()
        end
        return true, string.format("position 2 set to (%d, %d, %d)", pos.x, pos.y, pos.z)
        
    elseif we_cmd == "fixedpos" then
        -- Handle fixedpos command
        local target, x, y, z = params:match("^%s*set(%d)%s+([%-%.%d]+)[%s,]+([%-%.%d]+)[%s,]+([%-%.%d]+)%s*$")
        if target and x and y and z then
            local pos = {x = tonumber(x), y = tonumber(y), z = tonumber(z)}
            if target == "1" then
                worldedit.pos1[player_name] = pos
                if worldedit.mark_pos1 then
                    worldedit.mark_pos1(player_name)
                end
                -- Save positions after setting
                if player_name == VIRTUAL_PLAYER then
                    save_virtual_player_positions()
                end
                return true, string.format("position 1 set to (%d, %d, %d)", pos.x, pos.y, pos.z)
            elseif target == "2" then
                worldedit.pos2[player_name] = pos
                if worldedit.mark_pos2 then
                    worldedit.mark_pos2(player_name)
                end
                -- Save positions after setting
                if player_name == VIRTUAL_PLAYER then
                    save_virtual_player_positions()
                end
                return true, string.format("position 2 set to (%d, %d, %d)", pos.x, pos.y, pos.z)
            end
        end
        return false, "Invalid fixedpos format. Use: //fixedpos set1/set2 X Y Z"
        
    elseif we_cmd == "set" and worldedit.set then
        -- Direct API call for set command
        local pos1 = worldedit.pos1[player_name]
        local pos2 = worldedit.pos2[player_name]
        if not pos1 or not pos2 then
            return false, "Positions 1 and 2 must be set"
        end
        
        local node = params:match("^%s*(.+)%s*$")
        if not node then
            return false, "No node specified"
        end
        
        local count = worldedit.set(pos1, pos2, node)
        return true, count .. " nodes set"
    end
    
    -- Fallback not available for this command
    return false, "Command requires a real player object. Use //fixedpos or execute from in-game."
end

-- Add message to history
local function add_to_history(author, message, msg_type)
    table.insert(chat_history, {
        author = author,
        message = message,
        type = msg_type or 'chat',
        timestamp = os.time()
    })
    
    -- Keep history size limited
    while #chat_history > max_history do
        table.remove(chat_history, 1)
    end
end

-- Format chat message
function mcp_chat.format_chat_message(author, message)
    return string.format("%s <%s> %s", chat_prefix, author, message)
end

-- Send response to MCP server
local function send_response(data)
    if not http then
        minetest.log('error', '[mcp_chat] HTTP API not available')
        return
    end
    
    http.fetch({
        url = 'http://localhost:' .. tostring(port) .. '/response',
        timeout = timeout,
        post_data = minetest.write_json(data),
        extra_headers = {"Content-Type: application/json"}
    }, function(result)
        if not result.succeeded then
            minetest.log('error', '[mcp_chat] Failed to send response: ' .. (result.error or 'unknown error'))
        end
    end)
end

-- Handle incoming MCP requests
local function handle_mcp_request(data)
    if not data then return end
    
    local response = {
        success = false,
        error = "Unknown request type"
    }
    
    -- Handle chat messages
    if data.type == 'send_chat' then
        local message = data.message or ""
        local author = data.author or 'MCP'
        
        -- Check if this is a command (starts with /)
        if message:sub(1, 1) == "/" then
            -- This is a command, redirect to command execution
            local cmd_str = message:sub(2) -- Remove the leading /
            local cmd_name, params = cmd_str:match("^(%S+)%s*(.*)$")
            
            if cmd_name then
                -- Convert to command execution
                minetest.log('action', '[mcp_chat] Converting chat to command: /' .. cmd_name .. ' ' .. params)
                data.type = 'execute_command'
                data.command = cmd_name
                data.params = params or ""
                data.executor = VIRTUAL_PLAYER
                -- Continue to command execution below
            else
                response.error = "Invalid command format"
            end
        elseif relay_chat then
            -- Normal chat message
            local formatted_message = mcp_chat.format_chat_message(author, message)
            minetest.chat_send_all(minetest.colorize(text_color, formatted_message))
            add_to_history(author, message, 'mcp')
            minetest.log('action', '[mcp_chat] ' .. formatted_message)
            
            response.success = true
            response.message = "Chat message sent"
        else
            response.error = "Chat relay is disabled"
        end
    end
    
    -- Handle command execution (may come from original request or converted chat command)
    if data.type == 'execute_command' then
        minetest.log('action', '[mcp_chat] Executing command: /' .. (data.command or '?') .. ' ' .. (data.params or ''))
        if allow_commands then
            local cmd_name = data.command
            local params = data.params or ""
            local executor = data.executor or VIRTUAL_PLAYER
            
            -- Check if command exists
            local cmd = minetest.registered_chatcommands[cmd_name]
            if not cmd then
                response.error = "Unknown command: " .. cmd_name
            else
                -- Check privileges
                local required_privs = cmd.privs or {}
                local has_privs = true
                local missing_privs = {}
                
                -- For MCP execution, check against a special privilege set
                if next(required_privs) then
                    -- You might want to configure which privs MCP has
                    -- For now, we'll check if the command requires server priv
                    if required_privs.server then
                        has_privs = false
                        table.insert(missing_privs, "server")
                    end
                end
                
                if not has_privs then
                    response.error = "Missing privileges: " .. table.concat(missing_privs, ", ")
                else
                    -- Execute command with error handling
                    local success, result
                    local ok, cmd_success, cmd_result = pcall(cmd.func, executor, params)
                    
                    -- Debug what the command returned
                    minetest.log('action', '[mcp_chat] Command returned: ok=' .. tostring(ok) .. 
                        ', type1=' .. type(cmd_success) .. ', val1=' .. tostring(cmd_success) ..
                        ', type2=' .. type(cmd_result) .. ', val2=' .. tostring(cmd_result))
                    
                    if ok then
                        -- Command executed without Lua errors
                        -- Handle different return patterns from commands
                        if type(cmd_success) == "boolean" then
                            -- Command returned (success, message)
                            success = cmd_success
                            result = cmd_result
                        elseif type(cmd_success) == "string" then
                            -- Command returned just a message string - assume success
                            success = true
                            result = cmd_success
                        elseif cmd_success == nil then
                            -- Command returned nothing - assume success
                            success = true
                            result = "Command executed"
                        else
                            -- Unknown return type
                            success = true
                            result = tostring(cmd_success)
                        end
                    else
                        -- Command threw a Lua error
                        local error_msg = tostring(cmd_success)
                        if error_msg:find("player") and error_msg:find("nil") and worldedit then
                            -- Try WorldEdit API fallback for position commands
                            success, result = handle_worldedit_fallback(cmd_name, params, executor)
                        else
                            success = false
                            result = "Command error: " .. error_msg
                        end
                    end
                    response.success = success
                    
                    -- Enhance WorldEdit command feedback
                    if success and worldedit and cmd_name:sub(1, 1) == "/" then
                        -- WorldEdit commands start with double slash
                        local we_cmd = cmd_name:sub(2)
                        
                        -- Check for position-setting commands
                        if we_cmd == "pos1" or we_cmd == "1" or 
                           (we_cmd == "fixedpos" and params:match("^set1")) then
                            local pos1 = worldedit.pos1 and worldedit.pos1[executor]
                            if pos1 then
                                result = string.format("%s\nPosition 1 set to: (%.1f, %.1f, %.1f)", 
                                    result or "", pos1.x, pos1.y, pos1.z)
                            end
                            
                        elseif we_cmd == "pos2" or we_cmd == "2" or 
                               (we_cmd == "fixedpos" and params:match("^set2")) then
                            local pos2 = worldedit.pos2 and worldedit.pos2[executor]
                            if pos2 then
                                result = string.format("%s\nPosition 2 set to: (%.1f, %.1f, %.1f)", 
                                    result or "", pos2.x, pos2.y, pos2.z)
                            end
                            
                        -- Check for region operations
                        elseif result and result:match("^%d+ nodes") then
                            -- Many WorldEdit commands return "N nodes affected"
                            local pos1 = worldedit.pos1 and worldedit.pos1[executor]
                            local pos2 = worldedit.pos2 and worldedit.pos2[executor]
                            if pos1 and pos2 then
                                local volume = worldedit.volume(pos1, pos2)
                                result = string.format("%s (region volume: %d nodes)", result, volume)
                            end
                        end
                    end
                    
                    response.success = success
                    response.result = result or (success and "Command executed" or "Command failed")
                    
                    -- Clear error message if successful
                    if success then
                        response.error = nil
                    end
                    
                    -- Save positions after WorldEdit commands
                    if success and worldedit and executor == VIRTUAL_PLAYER then
                        save_virtual_player_positions()
                    end
                    
                    minetest.log('action', string.format('[mcp_chat] Command /%s %s executed by %s', 
                        cmd_name, params, executor))
                end
            end
        else
            response.error = "Command execution is disabled"
        end
        
    -- Get player list
    elseif data.type == 'get_players' then
        local players = {}
        for _, player in ipairs(minetest.get_connected_players()) do
            table.insert(players, {
                name = player:get_player_name(),
                pos = player:get_pos()
            })
        end
        response.success = true
        response.players = players
        
    -- Get chat history
    elseif data.type == 'get_chat_history' then
        local limit = data.limit or 20
        local history = {}
        local start_idx = math.max(1, #chat_history - limit + 1)
        
        for i = start_idx, #chat_history do
            table.insert(history, chat_history[i])
        end
        
        response.success = true
        response.history = history
        
    -- Get WorldEdit status
    elseif data.type == 'get_worldedit_status' then
        local player_name = data.player or VIRTUAL_PLAYER
        
        -- Check if worldedit exists
        if worldedit then
            local pos1 = worldedit.pos1 and worldedit.pos1[player_name]
            local pos2 = worldedit.pos2 and worldedit.pos2[player_name]
            
            response.success = true
            
            if pos1 then
                response.pos1 = {x = pos1.x, y = pos1.y, z = pos1.z}
            end
            
            if pos2 then
                response.pos2 = {x = pos2.x, y = pos2.y, z = pos2.z}
            end
            
            -- Calculate volume if both positions are set
            if pos1 and pos2 then
                response.volume = worldedit.volume(pos1, pos2)
            end
        else
            response.error = "WorldEdit not installed"
        end
    end
    
    -- Add request ID if provided
    if data.request_id then
        response.request_id = data.request_id
    end
    
    -- Debug log the response
    minetest.log('action', '[mcp_chat] Sending response: success=' .. tostring(response.success) .. 
        ', error=' .. tostring(response.error) .. ', result=' .. tostring(response.result))
    
    send_response(response)
end

-- HTTP polling
local timer = 0
minetest.register_globalstep(function(dtime)
    if not http then return end
    
    timer = timer + dtime
    if timer >= poll_interval then
        timer = 0
        
        http.fetch({
            url = 'http://localhost:' .. tostring(port) .. '/poll',
            timeout = timeout
        }, function(result)
            if result.succeeded and result.code == 200 and result.data then
                local data = minetest.parse_json(result.data)
                if data then
                    handle_mcp_request(data)
                end
            end
        end)
    end
end)

-- Intercept chat messages
if relay_chat then
    minetest.register_on_chat_message(function(name, message)
        -- Don't relay commands
        if message:sub(1, 1) == "/" then
            return false
        end
        
        add_to_history(name, message, 'player')
        
        -- Send to MCP server
        send_response({
            type = 'chat_message',
            author = name,
            message = message,
            timestamp = os.time()
        })
        
        return false
    end)
end

-- Initialize virtual player for WorldEdit
local function init_virtual_player()
    if worldedit then
        -- Initialize position tables if they don't exist
        worldedit.pos1 = worldedit.pos1 or {}
        worldedit.pos2 = worldedit.pos2 or {}
        
        -- Load saved positions for virtual player
        local saved_pos1 = storage:get_string("worldedit_pos1")
        local saved_pos2 = storage:get_string("worldedit_pos2")
        
        if saved_pos1 and saved_pos1 ~= "" then
            worldedit.pos1[VIRTUAL_PLAYER] = minetest.deserialize(saved_pos1)
            minetest.log('action', '[mcp_chat] Loaded saved pos1 for virtual player')
        else
            worldedit.pos1[VIRTUAL_PLAYER] = nil
        end
        
        if saved_pos2 and saved_pos2 ~= "" then
            worldedit.pos2[VIRTUAL_PLAYER] = minetest.deserialize(saved_pos2)
            minetest.log('action', '[mcp_chat] Loaded saved pos2 for virtual player')
        else
            worldedit.pos2[VIRTUAL_PLAYER] = nil
        end
        
        -- Override privilege checks for virtual player
        local original_check_player_privs = minetest.check_player_privs
        minetest.check_player_privs = function(name, privs)
            -- If checking privileges for our virtual player
            if name == VIRTUAL_PLAYER then
                -- Grant worldedit privilege
                if privs.worldedit then
                    return true
                end
                -- Deny server privilege for safety
                if privs.server then
                    return false
                end
                -- Grant other non-dangerous privileges
                return true
            end
            -- Use original function for real players
            return original_check_player_privs(name, privs)
        end
        
        minetest.log('action', '[mcp_chat] Virtual player "' .. VIRTUAL_PLAYER .. '" initialized for WorldEdit')
    end
end

-- Startup message
minetest.after(0, function()
    if http then
        -- Initialize virtual player
        init_virtual_player()
        
        send_response({
            type = 'server_status',
            status = 'started',
            port = port,
            features = {
                relay_chat = relay_chat,
                allow_commands = allow_commands,
                worldedit_enabled = worldedit ~= nil
            }
        })
        minetest.log('action', '[mcp_chat] MCP Chat integration started on port ' .. port)
    else
        minetest.log('error', '[mcp_chat] HTTP API not available! Add mcp_chat to secure.http_mods')
    end
end)

-- Shutdown message
minetest.register_on_shutdown(function()
    -- Save virtual player positions
    save_virtual_player_positions()
    
    if http then
        send_response({
            type = 'server_status',
            status = 'stopping'
        })
    end
end)