local ADDON_NAME = "MailboxCourier"

local DEFAULTS = {
    recipient = "",
    subject = "Auto mail",
    interval = 1.2,
    dryRun = false,
    autoConfirmUnknownRecipient = true,
    skipSoulbound = true,
    skipQuest = true,
    skipContainers = true,
    blacklist = {
        [6948] = true, -- Hearthstone
        [7005] = true, -- Skinning Knife
    },
}

local state = {
    running = false,
    mailboxOpen = false,
    queue = {},
    batch = {},
    batchCount = 0,
    sentItems = 0,
    sentMails = 0,
    nextAt = 0,
    waitingForSuccess = false,
    lastConfirmAt = 0,
    lastError = "",
}

local frame = CreateFrame("Frame")
local tooltip = CreateFrame("GameTooltip", "MailboxCourierTooltip", UIParent, "GameTooltipTemplate")
tooltip:SetOwner(UIParent, "ANCHOR_NONE")

local function copyDefaults(src, dst)
    dst = dst or {}
    for key, value in pairs(src) do
        if type(value) == "table" then
            dst[key] = copyDefaults(value, dst[key])
        elseif dst[key] == nil then
            dst[key] = value
        end
    end
    return dst
end

local function db()
    MailboxCourierDB = copyDefaults(DEFAULTS, MailboxCourierDB or {})
    return MailboxCourierDB
end

local function trim(text)
    return tostring(text or ""):gsub("^%s+", ""):gsub("%s+$", "")
end

local function log(message)
    DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99[" .. ADDON_NAME .. "]|r " .. tostring(message))
end

local function parseItemId(itemLink)
    if not itemLink then
        return nil
    end
    local itemId = itemLink:match("item:(%d+)")
    return itemId and tonumber(itemId) or nil
end

local function clearSendMailAttachments()
    ClearCursor()
    if ClearSendMail then
        ClearSendMail()
        return
    end
    for index = 1, 12 do
        if HasSendMailItem and HasSendMailItem(index) then
            ClickSendMailItemButton(index)
            ClearCursor()
        end
    end
end

local function tooltipHasText(bag, slot, needles)
    tooltip:ClearLines()
    tooltip:SetBagItem(bag, slot)
    for i = 1, tooltip:NumLines() do
        local line = _G["MailboxCourierTooltipTextLeft" .. i]
        local text = line and line:GetText()
        if text then
            for _, needle in ipairs(needles) do
                if needle and needle ~= "" and text:find(needle, 1, true) then
                    return true
                end
            end
        end
    end
    return false
end

local function isSoulbound(bag, slot)
    return tooltipHasText(bag, slot, {
        ITEM_SOULBOUND,
        "Soulbound",
        "灵魂绑定",
        "靈魂綁定",
    })
end

local function isQuestItem(bag, slot)
    return tooltipHasText(bag, slot, {
        ITEM_BIND_QUEST,
        "Quest Item",
        "任务物品",
        "任務物品",
    })
end

local function isContainerItem(itemId, itemLink)
    local name, link, quality, itemLevel, reqLevel, itemType, itemSubType, stackCount, equipLoc = GetItemInfo(itemId or itemLink or "")
    if equipLoc == "INVTYPE_BAG" then
        return true
    end
    if itemType == ITEM_CLASS_CONTAINER or itemType == "Container" or itemType == "容器" or itemType == "容器" then
        return true
    end
    return false
end

local function getContainerItemInfoCompat(bag, slot)
    local texture, count, locked, quality, readable, lootable, itemLink
    if C_Container and C_Container.GetContainerItemInfo then
        local info = C_Container.GetContainerItemInfo(bag, slot)
        if not info then
            return nil
        end
        texture = info.iconFileID
        count = info.stackCount
        locked = info.isLocked
        quality = info.quality
        readable = info.isReadable
        lootable = info.hasLoot
        itemLink = info.hyperlink
    else
        texture, count, locked, quality, readable, lootable, itemLink = GetContainerItemInfo(bag, slot)
        if not itemLink and GetContainerItemLink then
            itemLink = GetContainerItemLink(bag, slot)
        end
    end
    if not itemLink then
        return nil
    end
    return {
        texture = texture,
        count = count or 1,
        locked = locked,
        quality = quality,
        readable = readable,
        lootable = lootable,
        itemLink = itemLink,
        itemId = parseItemId(itemLink),
    }
end

local function getContainerNumSlotsCompat(bag)
    if C_Container and C_Container.GetContainerNumSlots then
        return C_Container.GetContainerNumSlots(bag) or 0
    end
    return GetContainerNumSlots(bag) or 0
end

local function shouldSkipItem(bag, slot, info)
    local cfg = db()
    local itemId = info.itemId

    if info.locked then
        return true, "locked"
    end
    if itemId and cfg.blacklist[itemId] then
        return true, "blacklisted"
    end
    if cfg.skipSoulbound and isSoulbound(bag, slot) then
        return true, "soulbound"
    end
    if cfg.skipQuest and isQuestItem(bag, slot) then
        return true, "quest"
    end
    if cfg.skipContainers and isContainerItem(itemId, info.itemLink) then
        return true, "container"
    end
    return false, nil
end

local function scanBags()
    local queue = {}
    local skipped = {}

    for bag = 0, 4 do
        for slot = 1, getContainerNumSlotsCompat(bag) do
            local info = getContainerItemInfoCompat(bag, slot)
            if info then
                local skip, reason = shouldSkipItem(bag, slot, info)
                if skip then
                    skipped[reason] = (skipped[reason] or 0) + 1
                else
                    table.insert(queue, {
                        bag = bag,
                        slot = slot,
                        itemId = info.itemId,
                        itemLink = info.itemLink,
                        count = info.count or 1,
                    })
                end
            end
        end
    end

    return queue, skipped
end

local function stop(reason)
    state.running = false
    state.waitingForSuccess = false
    state.queue = {}
    state.batch = {}
    state.batchCount = 0
    state.nextAt = 0
    clearSendMailAttachments()
    if reason and reason ~= "" then
        log(reason)
    end
end

local function popNextUnlockedItem()
    while #state.queue > 0 do
        local item = table.remove(state.queue, 1)
        local info = getContainerItemInfoCompat(item.bag, item.slot)
        if info and not info.locked and parseItemId(info.itemLink) == item.itemId then
            local skip = shouldSkipItem(item.bag, item.slot, info)
            if not skip then
                return item
            end
        end
    end
    return nil
end

local function attachNextBatch()
    clearSendMailAttachments()
    state.batch = {}
    state.batchCount = 0

    for attachment = 1, 12 do
        local item = popNextUnlockedItem()
        if not item then
            break
        end

        ClearCursor()
        PickupContainerItem(item.bag, item.slot)
        if CursorHasItem and not CursorHasItem() then
            log("无法拿起物品，跳过: " .. tostring(item.itemLink))
        else
            ClickSendMailItemButton(attachment)
            ClearCursor()
            table.insert(state.batch, item)
            state.batchCount = state.batchCount + 1
        end
    end

    return state.batchCount > 0
end

local function sendCurrentBatch()
    local cfg = db()
    local recipient = trim(cfg.recipient)
    if recipient == "" then
        stop("没有收件人。先输入 /mc to 角色名")
        return
    end

    local subject = trim(cfg.subject)
    if subject == "" then
        subject = "Auto mail"
    end
    subject = subject .. " " .. (state.sentMails + 1)

    if cfg.dryRun then
        state.sentItems = state.sentItems + state.batchCount
        state.sentMails = state.sentMails + 1
        log("dry-run: 模拟发送第 " .. state.sentMails .. " 封，附件 " .. state.batchCount .. " 件")
        clearSendMailAttachments()
        state.nextAt = GetTime() + cfg.interval
        return
    end

    state.waitingForSuccess = true
    state.nextAt = GetTime() + 8
    SendMail(recipient, subject, "")
    log("已提交第 " .. (state.sentMails + 1) .. " 封，附件 " .. state.batchCount .. " 件")
end

local function maybeConfirmMailPopup()
    local cfg = db()
    if not cfg.autoConfirmUnknownRecipient then
        return false
    end
    if not state.running or not state.waitingForSuccess then
        return false
    end
    if GetTime() - state.lastConfirmAt < 0.5 then
        return false
    end

    local recipient = trim(cfg.recipient)
    for i = 1, 4 do
        local popup = _G["StaticPopup" .. i]
        if popup and popup:IsShown() then
            local which = tostring(popup.which or "")
            local textFrame = _G["StaticPopup" .. i .. "Text"]
            local text = tostring(textFrame and textFrame:GetText() or "")
            local lowerText = text:lower()
            local looksLikeMailPopup =
                which:find("MAIL", 1, true)
                or lowerText:find("mail", 1, true)
                or lowerText:find("recipient", 1, true)
                or lowerText:find("send", 1, true)
                or (recipient ~= "" and text:find(recipient, 1, true))

            if looksLikeMailPopup then
                local button = _G["StaticPopup" .. i .. "Button1"]
                if button and (not button.IsEnabled or button:IsEnabled()) then
                    state.lastConfirmAt = GetTime()
                    button:Click()
                    log("Auto-confirmed mail warning popup.")
                    return true
                end
            end
        end
    end

    return false
end

local function process()
    if not state.running then
        return
    end
    if state.waitingForSuccess then
        maybeConfirmMailPopup()
    end
    if GetTime() < state.nextAt then
        return
    end
    if state.waitingForSuccess then
        stop("等待邮件发送成功超时，已停止。最后错误: " .. tostring(state.lastError))
        return
    end
    if not state.mailboxOpen then
        stop("邮箱没有打开，已停止。")
        return
    end

    if attachNextBatch() then
        sendCurrentBatch()
    else
        stop("邮寄完成，共发送 " .. state.sentMails .. " 封，" .. state.sentItems .. " 件物品。")
    end
end

local function beginSend()
    local cfg = db()
    local recipient = trim(cfg.recipient)
    if recipient == "" then
        log("没有收件人。用法: /mc to 角色名")
        return
    end
    if not state.mailboxOpen then
        log("邮箱还没打开。请先和邮箱交互。")
        return
    end

    state.queue = {}
    state.batch = {}
    state.batchCount = 0
    state.sentItems = 0
    state.sentMails = 0
    state.lastError = ""
    state.waitingForSuccess = false

    local skipped
    state.queue, skipped = scanBags()
    if #state.queue == 0 then
        log("没有找到可邮寄物品。")
        return
    end

    state.running = true
    state.nextAt = GetTime() + 0.2
    log("开始邮寄给 " .. recipient .. "，待发送 " .. #state.queue .. " 个背包格。")

    local parts = {}
    for reason, count in pairs(skipped or {}) do
        table.insert(parts, reason .. "=" .. count)
    end
    if #parts > 0 then
        log("已跳过: " .. table.concat(parts, ", "))
    end
end

local function showStatus()
    local cfg = db()
    log("收件人: " .. (trim(cfg.recipient) ~= "" and cfg.recipient or "未设置"))
    log("主题: " .. cfg.subject .. "；间隔: " .. tostring(cfg.interval) .. " 秒；dry-run: " .. tostring(cfg.dryRun))
    log("命令: /mc to 角色名, /mc send, /mc stop, /mc dryrun on|off, /mc black add|del itemID")
end

local function setRecipient(name)
    name = trim(name)
    if name == "" then
        log("用法: /mc to 角色名")
        return
    end
    db().recipient = name
    log("收件人已设置为: " .. name)
end

local function setSubject(subject)
    subject = trim(subject)
    if subject == "" then
        log("用法: /mc subject 邮件主题")
        return
    end
    db().subject = subject
    log("邮件主题已设置为: " .. subject)
end

local function setDryRun(value)
    value = trim(value):lower()
    if value == "on" or value == "1" or value == "true" then
        db().dryRun = true
    elseif value == "off" or value == "0" or value == "false" then
        db().dryRun = false
    else
        log("用法: /mc dryrun on|off")
        return
    end
    log("dry-run: " .. tostring(db().dryRun))
end

local function setAutoConfirm(value)
    value = trim(value):lower()
    if value == "on" or value == "1" or value == "true" then
        db().autoConfirmUnknownRecipient = true
    elseif value == "off" or value == "0" or value == "false" then
        db().autoConfirmUnknownRecipient = false
    else
        log("Usage: /mc autoconfirm on|off")
        return
    end
    log("auto-confirm unknown recipient: " .. tostring(db().autoConfirmUnknownRecipient))
end

local function updateBlacklist(rest)
    local action, idText = trim(rest):match("^(%S+)%s+(%S+)$")
    local itemId = tonumber(idText or "")
    if not action or not itemId then
        log("用法: /mc black add itemID 或 /mc black del itemID")
        return
    end
    action = action:lower()
    if action == "add" then
        db().blacklist[itemId] = true
        log("已加入黑名单 itemID: " .. itemId)
    elseif action == "del" or action == "remove" then
        db().blacklist[itemId] = nil
        log("已移出黑名单 itemID: " .. itemId)
    else
        log("用法: /mc black add itemID 或 /mc black del itemID")
    end
end

SLASH_MAILBOXCOURIER1 = "/mc"
SLASH_MAILBOXCOURIER2 = "/mailboxcourier"
SlashCmdList["MAILBOXCOURIER"] = function(msg)
    local command, rest = trim(msg):match("^(%S*)%s*(.-)$")
    command = (command or ""):lower()
    rest = rest or ""

    if command == "to" then
        setRecipient(rest)
    elseif command == "subject" then
        setSubject(rest)
    elseif command == "send" then
        beginSend()
    elseif command == "stop" then
        stop("已手动停止。")
    elseif command == "dryrun" then
        setDryRun(rest)
    elseif command == "autoconfirm" then
        setAutoConfirm(rest)
    elseif command == "black" or command == "blacklist" then
        updateBlacklist(rest)
    else
        showStatus()
    end
end

frame:RegisterEvent("ADDON_LOADED")
frame:RegisterEvent("MAIL_SHOW")
frame:RegisterEvent("MAIL_CLOSED")
frame:RegisterEvent("MAIL_SEND_SUCCESS")
frame:RegisterEvent("UI_ERROR_MESSAGE")

frame:SetScript("OnEvent", function(_, event, arg1, arg2)
    if event == "ADDON_LOADED" and arg1 == ADDON_NAME then
        db()
        log("已加载。输入 /mc 查看命令。")
    elseif event == "MAIL_SHOW" then
        state.mailboxOpen = true
        log("邮箱已打开。")
    elseif event == "MAIL_CLOSED" then
        state.mailboxOpen = false
        if state.running then
            stop("邮箱关闭，已停止。")
        end
    elseif event == "MAIL_SEND_SUCCESS" then
        state.sentItems = state.sentItems + state.batchCount
        state.sentMails = state.sentMails + 1
        state.waitingForSuccess = false
        state.batch = {}
        state.batchCount = 0
        clearSendMailAttachments()
        state.nextAt = GetTime() + db().interval
        log("第 " .. state.sentMails .. " 封发送成功，累计 " .. state.sentItems .. " 件。")
    elseif event == "UI_ERROR_MESSAGE" then
        state.lastError = tostring(arg2 or arg1 or "")
        if state.running and state.lastError ~= "" then
            log("错误: " .. state.lastError)
        end
    end
end)

frame:SetScript("OnUpdate", process)
