local addonName, addonTable = ...
---@type MK
local MK = addonTable.MK
local core = addonTable

core.AccountType = core.AccountType or {}
local AccountType = core.AccountType

local NETEASE_API_MAJOR = "NetEaseAPI-1.0"
local NETEASE_API_MIN_MINOR = 2026061902
local ADDON_ICON_PATH = "Interface\\AddOns\\" .. addonName .. "\\Art\\Ui\\Icon\\"
local NEWBIE_ICON_TEXTURE = ADDON_ICON_PATH .. "Newbie.png"
local VETERAN_ICON_TEXTURE = ADDON_ICON_PATH .. "Veteran.png"
local STAR_ICON_TEXTURE = ADDON_ICON_PATH .. "Star.png"
local LEADER_ICON_TEXTURE = ADDON_ICON_PATH .. "Leader.png"
local ACCOUNT_TYPE_ICON_SIZE = 18
local ACCOUNT_TYPE_GREEN_TEXT_COLOR = { 0.18, 1, 0.22, 1 }
local ACCOUNT_TYPE_GOLD_TEXT_COLOR = { 1, 0.82, 0, 1 }
local ACCOUNT_TYPE_RED_TEXT_COLOR = { 1, 0.08, 0.05, 1 }
local ACCOUNT_TYPE_PENDING_TEXT_COLOR = { 0.62, 0.75, 1, 1 }
local ACCOUNT_TYPE_SERVICE_CONNECTION_MISSING_TEXT = "网易 API 离线"
local ACCOUNT_TYPE_API_NO_RESPONSE_TEXT = "网易 API 无响应"
local ACCOUNT_TYPE_API_FAULT_TEXT = "网易 API 故障"
local ACCOUNT_TYPE_API_STARTING_TEXT = "网易 API 初始化中"
local ACCOUNT_TYPE_SERVICE_CONNECT_TIMEOUT_SECONDS = 30
local ACCOUNT_TYPE_SERVICE_FAILURE_COOLDOWN_SECONDS = 5 * 60

local ACCOUNT_TYPE_PRIORITY = {
    "newbie",
    "leader",
    "star",
    "veteran",
}

local ACCOUNT_TYPE_STATUS = {
    pending = true,
    no_response = true,
    unavailable = true,
    unknown = true,
    newbie = true,
    leader = true,
    star = true,
    veteran = true,
}

local RefreshAccountTypeDisplays

local function TrimString(value)
    if type(value) ~= "string" then
        return nil
    end

    value = value:gsub("^%s+", ""):gsub("%s+$", "")
    if value == "" then
        return nil
    end
    return value
end

local function IsSupportedNetEaseAPI(api)
    if not (type(api) == "table" and type(api.GetMinorVersion) == "function") then
        return false
    end

    local ok, minor = pcall(api.GetMinorVersion, api)
    return ok and (tonumber(minor) or 0) >= NETEASE_API_MIN_MINOR
end

local function EnsureNetEaseAPIStorage(api)
    if not (type(api) == "table" and type(api.SetStorage) == "function") then
        return
    end

    _G.NetEaseAPIDB = type(_G.NetEaseAPIDB) == "table" and _G.NetEaseAPIDB or {}
    pcall(api.SetStorage, api, _G.NetEaseAPIDB)
end

local function GetNetEaseAPI()
    if type(LibStub) == "table" and type(LibStub.GetLibrary) == "function" then
        local ok, api = pcall(LibStub.GetLibrary, LibStub, NETEASE_API_MAJOR, true)
        if ok and IsSupportedNetEaseAPI(api) then
            EnsureNetEaseAPIStorage(api)
            return api
        end
        return nil
    end

    if type(LibStub) ~= "function" then
        return nil
    end

    local ok, api = pcall(LibStub, NETEASE_API_MAJOR, true)
    if ok and IsSupportedNetEaseAPI(api) then
        EnsureNetEaseAPIStorage(api)
        return api
    end
    return nil
end

local function GetAddonVersion()
    if C_AddOns and type(C_AddOns.GetAddOnMetadata) == "function" then
        local ok, version = pcall(C_AddOns.GetAddOnMetadata, addonName, "Version")
        if ok then
            return version
        end
    end
    return nil
end

local function GetAccountTypeTime()
    if GetTime then
        return GetTime()
    end
    return time and time() or 0
end

local function GetAccountTypeStats(api)
    if api and type(api.GetStats) == "function" then
        local ok, stats = pcall(api.GetStats, api)
        if ok and type(stats) == "table" then
            return stats
        end
    end
    return {}
end

local function GetConnectionDiagnostic(api, stats)
    if api and type(api.GetConnectionDiagnostic) == "function" then
        local ok, diagnostic = pcall(api.GetConnectionDiagnostic, api)
        if ok and type(diagnostic) == "table" then
            return diagnostic
        end
    end

    stats = type(stats) == "table" and stats or {}
    if stats.connectionStatus or stats.connectionReason or stats.serviceConnectionMissing ~= nil then
        return {
            status = stats.connectionStatus,
            reason = stats.connectionReason,
            message = stats.connectionMessage,
            connected = stats.serviceConnected,
            serviceConnectionMissing = stats.serviceConnectionMissing,
            socketStatus = stats.socketStatus,
            socketStatusText = stats.socketStatusText,
            connectTarget = stats.connectTarget,
            target = stats.target,
            waitingSeconds = stats.waitingSeconds,
        }
    end
    return nil
end

local function IsNetEaseServiceStarting(status, reason, diagnostic)
    return status == "starting"
        or reason == "not_started"
        or (diagnostic and (diagnostic.status == "not_started" or diagnostic.reason == "not_started"))
end

local function GetNetEaseServiceStatus(api, stats, fullName)
    if api and type(api.GetServiceStatus) == "function" then
        local ok, status, message, reason, diagnostic = pcall(api.GetServiceStatus, api, fullName)
        if ok and status then
            if IsNetEaseServiceStarting(status, reason, diagnostic) then
                return "starting", message or ACCOUNT_TYPE_API_STARTING_TEXT, reason or "not_started", diagnostic
            end
            return status, message, reason, diagnostic
        end
    end

    stats = type(stats) == "table" and stats or {}
    if stats.serviceStatus then
        local diagnostic = GetConnectionDiagnostic(api, stats)
        if IsNetEaseServiceStarting(stats.serviceStatus, stats.serviceReason, diagnostic) then
            return "starting", stats.serviceMessage or ACCOUNT_TYPE_API_STARTING_TEXT, stats.serviceReason or "not_started", diagnostic
        end
        return stats.serviceStatus, stats.serviceMessage, stats.serviceReason, diagnostic
    end

    local diagnostic = GetConnectionDiagnostic(api, stats)
    if diagnostic then
        if diagnostic.connected == true then
            return "ok", nil, diagnostic.reason, diagnostic
        end
        if diagnostic.serviceConnectionMissing
            or diagnostic.status == "connecting"
            or diagnostic.status == "missing_handler"
            or diagnostic.status == "not_listening"
        then
            return "offline", ACCOUNT_TYPE_SERVICE_CONNECTION_MISSING_TEXT, diagnostic.reason or "waiting_connection", diagnostic
        end
        if diagnostic.status == "not_started" then
            return "starting", diagnostic.message or ACCOUNT_TYPE_API_STARTING_TEXT, diagnostic.reason or "not_started", diagnostic
        end
        if diagnostic.status == "unsupported" then
            return "fault", ACCOUNT_TYPE_API_FAULT_TEXT, diagnostic.reason or diagnostic.status, diagnostic
        end
    end
    return nil, nil, nil, diagnostic
end

local function MapNetEaseServiceStatus(status, message, reason)
    if status == "starting" then
        return "starting", reason or "not_started", message or ACCOUNT_TYPE_API_STARTING_TEXT
    end
    if status == "offline" then
        return "service_connection_missing", reason or "waiting_connection", message or ACCOUNT_TYPE_SERVICE_CONNECTION_MISSING_TEXT
    end
    if status == "no_response" then
        return "no_response", reason or "timeout", message or ACCOUNT_TYPE_API_NO_RESPONSE_TEXT
    end
    if status == "fault" then
        return "unavailable", reason or "fault", message or ACCOUNT_TYPE_API_FAULT_TEXT
    end
    return nil, nil, nil
end

local function ClearServiceConnectionFailure()
    AccountType.serviceConnectionStartedAt = nil
    AccountType.serviceFailureReason = nil
    AccountType.serviceFailureMessage = nil
    AccountType.serviceFailureAt = nil
    AccountType.serviceFailureUntil = nil
end

local function GetServiceFailureCooldownRemaining()
    local failureUntil = tonumber(AccountType.serviceFailureUntil)
    if not failureUntil then
        return 0
    end

    local remaining = failureUntil - GetAccountTypeTime()
    if remaining <= 0 then
        ClearServiceConnectionFailure()
        return 0
    end
    return remaining
end

local function IsServiceFailureCooldownActive()
    return GetServiceFailureCooldownRemaining() > 0
end

local function MarkServiceConnectionFailure(reason, message)
    local now = GetAccountTypeTime()
    AccountType.serviceConnectionStartedAt = nil
    AccountType.serviceFailureReason = reason or "service_unreachable"
    AccountType.serviceFailureMessage = message
    AccountType.serviceFailureAt = now
    AccountType.serviceFailureUntil = now + ACCOUNT_TYPE_SERVICE_FAILURE_COOLDOWN_SECONDS
end

local function GetServiceFailureStatus()
    local reason = AccountType.serviceFailureReason or "service_unreachable"
    if reason == "waiting_connection" then
        return "service_connection_missing", reason, AccountType.serviceFailureMessage or ACCOUNT_TYPE_SERVICE_CONNECTION_MISSING_TEXT
    end
    return "service_unreachable", reason, AccountType.serviceFailureMessage
end

local function ScheduleServiceConnectionCheck()
    if AccountType.serviceConnectionCheckScheduled or IsServiceFailureCooldownActive() then
        return
    end
    if not (C_Timer and C_Timer.After) then
        return
    end

    local startedAt = tonumber(AccountType.serviceConnectionStartedAt)
    if not startedAt then
        return
    end

    local elapsed = GetAccountTypeTime() - startedAt
    local delay = math.max(1, ACCOUNT_TYPE_SERVICE_CONNECT_TIMEOUT_SECONDS - elapsed)
    AccountType.serviceConnectionCheckScheduled = true
    C_Timer.After(delay, function()
        AccountType.serviceConnectionCheckScheduled = false
        if IsServiceFailureCooldownActive() then
            return
        end

        local api = GetNetEaseAPI()
        local stats = GetAccountTypeStats(api)
        local diagnostic = GetConnectionDiagnostic(api, stats)
        if stats.connected == true or (diagnostic and diagnostic.connected == true) then
            ClearServiceConnectionFailure()
            return
        end

        local currentStartedAt = tonumber(AccountType.serviceConnectionStartedAt)
        if stats.started == true and currentStartedAt and GetAccountTypeTime() - currentStartedAt >= ACCOUNT_TYPE_SERVICE_CONNECT_TIMEOUT_SECONDS then
            MarkServiceConnectionFailure("service_unreachable", ACCOUNT_TYPE_SERVICE_CONNECTION_MISSING_TEXT)
            RefreshAccountTypeDisplays("ServiceConnectionFailed", "service_unreachable")
            return
        end

        if stats.started == true and currentStartedAt then
            ScheduleServiceConnectionCheck()
        end
    end)
end

local function NoteServiceConnectionAttempt(api, stats, fullName)
    stats = type(stats) == "table" and stats or {}
    local diagnostic = GetConnectionDiagnostic(api, stats)
    local serviceStatus, serviceMessage, serviceReason = GetNetEaseServiceStatus(api, stats, fullName)
    if serviceStatus == "ok" or stats.connected == true or (diagnostic and diagnostic.connected == true) then
        ClearServiceConnectionFailure()
        return nil, nil, nil
    end

    local mappedStatus, mappedReason, mappedMessage = MapNetEaseServiceStatus(serviceStatus, serviceMessage, serviceReason)
    if mappedStatus == "service_connection_missing" then
        MarkServiceConnectionFailure(mappedReason, mappedMessage)
        return mappedStatus, mappedReason, mappedMessage
    elseif mappedStatus then
        return mappedStatus, mappedReason, mappedMessage
    end

    if diagnostic and diagnostic.serviceConnectionMissing == true then
        MarkServiceConnectionFailure(diagnostic.reason or "waiting_connection", ACCOUNT_TYPE_SERVICE_CONNECTION_MISSING_TEXT)
        return "service_connection_missing", diagnostic.reason or "waiting_connection", ACCOUNT_TYPE_SERVICE_CONNECTION_MISSING_TEXT
    end

    if IsServiceFailureCooldownActive() then
        return GetServiceFailureStatus()
    end

    if stats.started == true then
        local now = GetAccountTypeTime()
        AccountType.serviceConnectionStartedAt = AccountType.serviceConnectionStartedAt or now
        if now - AccountType.serviceConnectionStartedAt >= ACCOUNT_TYPE_SERVICE_CONNECT_TIMEOUT_SECONDS then
            MarkServiceConnectionFailure("service_unreachable", ACCOUNT_TYPE_SERVICE_CONNECTION_MISSING_TEXT)
            return "service_unreachable", "service_unreachable", ACCOUNT_TYPE_SERVICE_CONNECTION_MISSING_TEXT
        end
        ScheduleServiceConnectionCheck()
        return "connecting", "waiting_connection", "等待服务连接"
    end

    return nil, nil, nil
end

function AccountType.SplitFullName(name)
    name = TrimString(name)
    if not name then
        return nil, nil
    end

    local shortName, realm = name:match("^([^-]+)%-(.+)$")
    if shortName and realm then
        return TrimString(shortName) or name, TrimString(realm)
    end
    return name, nil
end

function AccountType.NormalizeIdentity(name, realm)
    name = TrimString(name)
    realm = TrimString(realm)
    if not name then
        return nil, nil, nil
    end

    local shortName, embeddedRealm = AccountType.SplitFullName(name)
    if shortName then
        name = shortName
        realm = realm or embeddedRealm
    end

    local key = realm and (name .. "-" .. realm) or name
    return name, realm, key
end

local function BuildFullName(name, realm)
    local normalizedName, normalizedRealm, key = AccountType.NormalizeIdentity(name, realm)
    if not normalizedName then
        return nil, nil, nil
    end
    return normalizedRealm and (normalizedName .. "-" .. normalizedRealm) or normalizedName, normalizedName, normalizedRealm, key
end

function AccountType.GetIdentityFromCharacterEntry(characterEntry)
    if type(characterEntry) ~= "table" then
        return nil, nil, nil
    end

    local character = characterEntry.data
    if type(character) == "table" then
        return AccountType.NormalizeIdentity(character.name or characterEntry.key, character.realm)
    end
    return AccountType.NormalizeIdentity(characterEntry.name or characterEntry.key, characterEntry.realm)
end

function AccountType.GetIdentityFromName(name, realm)
    return AccountType.NormalizeIdentity(name, realm)
end

local function GetIdentityFromUnit(unit)
    if type(unit) ~= "table" then
        return nil, nil, nil
    end

    if unit.characterEntry then
        return AccountType.GetIdentityFromCharacterEntry(unit.characterEntry)
    end
    if unit.entry then
        return AccountType.GetIdentityFromCharacterEntry(unit.entry)
    end
    return AccountType.NormalizeIdentity(unit.name, unit.realm)
end

function AccountType.MakeCharacterQueryEntry(name, realm, extraData)
    local normalizedName, normalizedRealm, key = AccountType.NormalizeIdentity(name, realm)
    if not normalizedName then
        return nil
    end

    local data = {
        name = normalizedName,
        realm = normalizedRealm,
    }
    if type(extraData) == "table" then
        for dataKey, value in pairs(extraData) do
            data[dataKey] = value
        end
    end
    return {
        key = key,
        data = data,
        mkAccountTypeKey = key,
    }
end

local function ShouldIncludeQueryEntry(name, realm, options)
    if not name then
        return false
    end
    if options and options.requireRealm == true and not realm then
        return false
    end
    return true
end

function AccountType.NormalizeQueryEntries(entries, options)
    local normalized = {}
    if type(entries) ~= "table" then
        return normalized
    end

    local seen = {}
    for _, entry in ipairs(entries) do
        if not (options and type(options.skip) == "function" and options.skip(entry) == true) then
            local name, realm, key
            if type(entry) == "table" then
                if type(options and options.getIdentity) == "function" then
                    name, realm, key = options.getIdentity(entry)
                    name, realm, key = AccountType.NormalizeIdentity(name, realm or select(2, AccountType.SplitFullName(key)))
                else
                    name, realm, key = AccountType.GetIdentityFromCharacterEntry(entry)
                    if not name then
                        name, realm, key = AccountType.NormalizeIdentity(entry.name, entry.realm)
                    end
                end
            elseif type(entry) == "string" then
                name, realm, key = AccountType.NormalizeIdentity(entry)
            end

            if ShouldIncludeQueryEntry(name, realm, options) and key and not seen[key] then
                seen[key] = true
                local queryEntry = AccountType.MakeCharacterQueryEntry(name, realm)
                if queryEntry then
                    normalized[#normalized + 1] = queryEntry
                end
            end
        end
    end

    return normalized
end

local function IsPendingNetEaseStatus(status)
    return status == "pending" or status == "cooldown" or status == "starting"
end

function AccountType.QueueQueries(entries, options)
    local api = GetNetEaseAPI()
    if not (api and type(api.QueryPlayerIdentities) == "function") then
        return 0, {}, {}, {}
    end

    local queryEntries = AccountType.NormalizeQueryEntries(entries, options)
    local names = {}
    local keyByName = {}
    for _, queryEntry in ipairs(queryEntries) do
        local character = queryEntry.data
        local fullName = character and BuildFullName(character.name, character.realm)
        if fullName then
            names[#names + 1] = fullName
            keyByName[fullName] = queryEntry.mkAccountTypeKey or queryEntry.key
        end
    end

    if #names > 0 and IsServiceFailureCooldownActive() and not (options and options.force == true) then
        return 0, {}, queryEntries, {}
    end

    local queuedCount = api:QueryPlayerIdentities(names, options and options.force) or 0
    local stats = GetAccountTypeStats(api)
    local serviceStatus = nil
    if queuedCount > 0 or (stats.started == true and stats.connected ~= true) then
        serviceStatus = NoteServiceConnectionAttempt(api, stats)
    end
    local queuedKeys = {}
    local waitingKeys = {}
    for _, fullName in ipairs(names) do
        local status, statusReason
        if api.GetPlayerIdentityStatus then
            status, statusReason = api:GetPlayerIdentityStatus(fullName)
        end
        local key = keyByName[fullName]
        local isServiceMissing = serviceStatus == "service_connection_missing" or statusReason == "waiting_connection"
        local isServiceBlocked = isServiceMissing
            or serviceStatus == "service_unreachable"
            or serviceStatus == "no_response"
            or serviceStatus == "unavailable"
        if key and IsPendingNetEaseStatus(status) and not isServiceBlocked then
            waitingKeys[key] = true
            if status == "pending" then
                queuedKeys[key] = true
            end
        end
    end

    return queuedCount, queuedKeys, queryEntries, waitingKeys
end

local function AddAccountTypeBadge(badges, badge)
    if type(badges) ~= "table" or type(badge) ~= "table" or not badge.key then
        return
    end
    badges[#badges + 1] = badge
end

local function ApplyPrimaryAccountTypeBadge(info, badges)
    if type(info) ~= "table" or type(badges) ~= "table" then
        return
    end

    local primary = badges[1]
    if not primary then
        info.status = "unknown"
        info.text = ""
        info.compactText = ""
        info.textColor = ACCOUNT_TYPE_PENDING_TEXT_COLOR
        return
    end

    info.status = primary.key
    info.texture = primary.texture
    info.text = primary.text
    info.compactText = primary.compactText or primary.text
    info.textColor = primary.textColor
    info.width = primary.width
    info.height = primary.height
    info.gap = primary.gap
end

local function NormalizePositiveInteger(value)
    local numberValue = tonumber(value)
    if not numberValue then
        return nil
    end
    numberValue = math.floor(numberValue)
    if numberValue <= 0 then
        return nil
    end
    return numberValue
end

local function GetFailureText(status, reason)
    if reason == "waiting_connection"
        or reason == "service_unreachable"
        or reason == "service_target_not_found"
        or status == "service_unreachable"
        or status == "service_connection_missing" then
        return ACCOUNT_TYPE_SERVICE_CONNECTION_MISSING_TEXT
    end
    if reason == "timeout" or reason == "no_response" or status == "failed" or status == "no_response" then
        return ACCOUNT_TYPE_API_NO_RESPONSE_TEXT
    end
    return ACCOUNT_TYPE_API_FAULT_TEXT
end

local function BuildAccountTypeInfo(fullName, identity, status, options, failureReason)
    options = type(options) == "table" and options or {}
    local isWorking = status == "pending" or status == "connecting" or status == "starting"

    local badges = {}
    local info = {
        queryPending = isWorking,
        activeQueryPending = isWorking,
        failureReason = failureReason,
        badges = badges,
        width = ACCOUNT_TYPE_ICON_SIZE,
        height = ACCOUNT_TYPE_ICON_SIZE,
    }

    if not fullName then
        info.status = "unknown"
        info.text = ""
        info.compactText = ""
        info.textColor = ACCOUNT_TYPE_PENDING_TEXT_COLOR
        return info
    end

    if type(identity) ~= "table" then
        if isWorking then
            info.status = "pending"
            local pendingText = status == "starting" and "" or (status == "connecting" and "服务连接中" or "···")
            info.text = pendingText
            info.compactText = pendingText
            info.textColor = ACCOUNT_TYPE_PENDING_TEXT_COLOR
            return info
        end

        if status == "failed" or status == "service_unreachable" or status == "service_connection_missing" then
            local failureText = GetFailureText(status, failureReason)
            info.status = "no_response"
            info.text = failureText
            info.compactText = failureText
            info.textColor = ACCOUNT_TYPE_RED_TEXT_COLOR
            return info
        end

        if status == "unsupported" and options.returnUnavailable ~= false then
            local unavailableText = GetFailureText(status, failureReason)
            info.status = "unavailable"
            info.text = unavailableText
            info.compactText = unavailableText
            info.textColor = ACCOUNT_TYPE_RED_TEXT_COLOR
            return info
        end

        info.status = "unknown"
        info.text = ""
        info.compactText = ""
        info.textColor = ACCOUNT_TYPE_PENDING_TEXT_COLOR
        return info
    end

    local now = time and time() or 0
    local newbieExpireTime = tonumber(identity.newbieExpireTime)
    local isNewbie = identity.isNewbie == true and (not newbieExpireTime or now < newbieExpireTime)
    local newbieExpired = identity.isNewbie == true and newbieExpireTime and now >= newbieExpireTime
    local leaderLevel = NormalizePositiveInteger(identity.level)
    local starLevel = NormalizePositiveInteger(identity.starLevel)

    info.level = leaderLevel
    info.starLevel = starLevel
    info.isNewbie = isNewbie
    info.newbieExpired = newbieExpired == true

    if isNewbie then
        AddAccountTypeBadge(badges, {
            key = "newbie",
            texture = NEWBIE_ICON_TEXTURE,
            text = "大秘新兵",
            compactText = "新兵",
            textColor = ACCOUNT_TYPE_GREEN_TEXT_COLOR,
            width = ACCOUNT_TYPE_ICON_SIZE,
            height = ACCOUNT_TYPE_ICON_SIZE,
        })
    else
        if leaderLevel then
            AddAccountTypeBadge(badges, {
                key = "leader",
                texture = LEADER_ICON_TEXTURE,
                text = "火车头 Lv" .. tostring(leaderLevel),
                compactText = "Lv" .. tostring(leaderLevel),
                textColor = ACCOUNT_TYPE_GOLD_TEXT_COLOR,
                width = ACCOUNT_TYPE_ICON_SIZE,
                height = ACCOUNT_TYPE_ICON_SIZE,
                level = leaderLevel,
            })
        end
        if starLevel then
            AddAccountTypeBadge(badges, {
                key = "star",
                texture = STAR_ICON_TEXTURE,
                text = "星团长 Lv" .. tostring(starLevel),
                compactText = "Lv" .. tostring(starLevel),
                textColor = ACCOUNT_TYPE_GOLD_TEXT_COLOR,
                width = ACCOUNT_TYPE_ICON_SIZE,
                height = ACCOUNT_TYPE_ICON_SIZE,
                level = starLevel,
            })
        end
        AddAccountTypeBadge(badges, {
            key = "veteran",
            texture = VETERAN_ICON_TEXTURE,
            text = "老兵",
            compactText = "老兵",
            textColor = ACCOUNT_TYPE_GOLD_TEXT_COLOR,
            width = ACCOUNT_TYPE_ICON_SIZE,
            height = ACCOUNT_TYPE_ICON_SIZE,
        })
    end

    ApplyPrimaryAccountTypeBadge(info, badges)
    return info
end

local function GetBadgeStatus(accountTypeInfo, includeVeteran)
    if type(accountTypeInfo) ~= "table" then
        return nil
    end

    if type(accountTypeInfo.badges) ~= "table" then
        return nil
    end

    for _, status in ipairs(ACCOUNT_TYPE_PRIORITY) do
        if status ~= "veteran" or includeVeteran ~= false then
            for _, badge in ipairs(accountTypeInfo.badges) do
                if badge and badge.key == status then
                    return status
                end
            end
        end
    end
    return nil
end

function AccountType.GetAccountTypeInfoState(accountTypeInfo, options)
    options = type(options) == "table" and options or {}
    if not accountTypeInfo then
        return nil
    end

    local status = accountTypeInfo.status
    if status == "pending" or status == "no_response" or status == "unknown" or status == "unavailable" then
        return status
    end
    if status == "veteran" and options.includeVeteran == false then
        return nil
    end
    if status == "newbie" or status == "leader" or status == "star" or status == "veteran" then
        return status
    end

    return GetBadgeStatus(accountTypeInfo, options.includeVeteran)
end

function AccountType.IsAccountTypeStatus(status)
    return ACCOUNT_TYPE_STATUS[status] == true
end

function AccountType.GetAccountState(name, realm, options)
    options = type(options) == "table" and options or {}
    local normalizedName, normalizedRealm, key = AccountType.NormalizeIdentity(name, realm)
    if not normalizedName then
        return {
            status = "unknown",
            name = nil,
            realm = nil,
        }
    end

    if options.requireRealm == true and not normalizedRealm then
        return {
            status = options.returnUnavailable == false and "unknown" or "unavailable",
            name = normalizedName,
            realm = normalizedRealm,
            key = normalizedName,
        }
    end

    local fullName = normalizedRealm and (normalizedName .. "-" .. normalizedRealm) or normalizedName
    local testIdentity = AccountType.testIdentities and AccountType.testIdentities[fullName]
    if type(testIdentity) == "table" then
        local accountTypeInfo = BuildAccountTypeInfo(fullName, testIdentity, "fresh", options)
        local accountStatus = AccountType.GetAccountTypeInfoState(accountTypeInfo, options) or "unknown"
        return {
            status = accountStatus,
            accountTypeInfo = accountTypeInfo,
            name = normalizedName,
            realm = normalizedRealm,
            key = key,
        }
    end

    local api = GetNetEaseAPI()
    if not api then
        return {
            status = "unknown",
            name = normalizedName,
            realm = normalizedRealm,
            key = key,
        }
    end

    local identity = api.GetPlayerIdentity and api:GetPlayerIdentity(fullName) or nil
    local status, statusReason
    if api.GetPlayerIdentityStatus then
        status, statusReason = api:GetPlayerIdentityStatus(fullName)
    end
    status = status or "missing"
    local failureReason = statusReason
    if type(identity) ~= "table" then
        if IsServiceFailureCooldownActive() then
            status, failureReason = GetServiceFailureStatus()
        elseif statusReason == "not_started" then
            local stats = GetAccountTypeStats(api)
            local serviceStatus, serviceReason = NoteServiceConnectionAttempt(api, stats, fullName)
            status = serviceStatus or "starting"
            failureReason = serviceReason or "not_started"
        elseif statusReason == "waiting_connection" then
            local stats = GetAccountTypeStats(api)
            local serviceStatus, serviceReason = NoteServiceConnectionAttempt(api, stats, fullName)
            status = serviceStatus or "service_connection_missing"
            failureReason = serviceReason or "waiting_connection"
        elseif status == "pending" then
            local stats = GetAccountTypeStats(api)
            local serviceStatus, serviceReason = NoteServiceConnectionAttempt(api, stats, fullName)
            if serviceStatus then
                status = serviceStatus
                failureReason = serviceReason
            end
        elseif status == "failed" then
            failureReason = "timeout"
        elseif status == "missing" then
            local stats = GetAccountTypeStats(api)
            local serviceStatus, serviceReason = NoteServiceConnectionAttempt(api, stats, fullName)
            if serviceStatus == "service_unreachable"
                or serviceStatus == "service_connection_missing"
                or serviceStatus == "no_response"
                or serviceStatus == "unavailable"
                or serviceStatus == "starting" then
                status = serviceStatus
                failureReason = serviceReason
            end
        end
    end
    local accountTypeInfo = BuildAccountTypeInfo(fullName, identity, status, options, failureReason)
    local accountStatus = AccountType.GetAccountTypeInfoState(accountTypeInfo, options) or "unknown"
    return {
        status = accountStatus,
        accountTypeInfo = accountTypeInfo,
        queryStatus = status,
        queryFailureReason = failureReason,
        name = normalizedName,
        realm = normalizedRealm,
        key = key,
    }
end

local function IsAllowedAccountStatus(status, allowedStatuses)
    if type(allowedStatuses) ~= "table" then
        return status and status ~= "unknown"
    end
    return allowedStatuses[status] == true
end

function AccountType.ResolveUnitStatus(unit, options)
    unit = type(unit) == "table" and unit or {}
    options = type(options) == "table" and options or {}
    local name, realm, key = GetIdentityFromUnit(unit)

    if options.includeOffline == true and unit.isOffline == true then
        return {
            status = "offline",
            name = name,
            realm = realm,
            key = key,
        }
    end

    if options.includeBlacklist ~= false then
        local isBlacklisted = unit.isBlacklisted == true
        local blacklistRecord = unit.blacklistRecord
        if not isBlacklisted and options.checkBlacklist ~= false and MK and MK.IsPlayerBlacklisted and name then
            isBlacklisted, blacklistRecord = MK:IsPlayerBlacklisted(name, realm)
        end
        if isBlacklisted then
            return {
                status = "blacklist",
                name = name,
                realm = realm,
                key = key,
                blacklistRecord = blacklistRecord,
            }
        end
    end

    if options.includeLeaver ~= false and unit.isLeaver == true then
        return {
            status = "leaver",
            name = name,
            realm = realm,
            key = key,
        }
    end

    if options.includeAccount ~= false then
        local accountState = AccountType.GetAccountState(name, realm, {
            requireRealm = options.requireRealm == true,
            returnUnavailable = options.returnUnavailable,
            includeVeteran = options.includeVeteran,
        })
        if IsAllowedAccountStatus(accountState.status, options.accountStatuses) then
            return accountState
        end
    end

    if options.includeRelationship == true and unit.relationship then
        return {
            status = unit.relationship,
            relationship = unit.relationship,
            name = name,
            realm = realm,
            key = key,
        }
    end

    return {
        status = "unknown",
        name = name,
        realm = realm,
        key = key,
    }
end

RefreshAccountTypeDisplays = function(eventName, name, reason)
    if eventName == "ServerStatusChanged" and name == true then
        ClearServiceConnectionFailure()
    elseif eventName == "PlayerIdentityFailed" and name == nil and reason then
        MarkServiceConnectionFailure(reason)
    end

    local refreshDetail = type(name) == "string" and name or eventName
    AccountType.pendingRefreshDetail = refreshDetail or AccountType.pendingRefreshDetail or "PlayerIdentity"
    if AccountType.refreshScheduled then
        return
    end

    AccountType.refreshScheduled = true
    local function runRefresh()
        AccountType.refreshScheduled = false
        local detail = AccountType.pendingRefreshDetail
        AccountType.pendingRefreshDetail = nil
        local owner = AccountType.owner or MK
        if owner and owner.RefreshMainWindow then
            owner:RefreshMainWindow("NetEaseAPI", detail or "PlayerIdentity")
        end
    end

    if C_Timer and C_Timer.After then
        C_Timer.After(0, runRefresh)
    else
        runRefresh()
    end
end

local function RegisterNetEaseCallback(api, eventName)
    if not (api and api.RegisterCallback) then
        return
    end

    if api.callbacks then
        api:RegisterCallback(AccountType, eventName, RefreshAccountTypeDisplays)
    else
        api.RegisterCallback(AccountType, eventName, RefreshAccountTypeDisplays)
    end
end

local function UnregisterNetEaseCallbacks(api)
    if not (api and api.UnregisterAllCallbacks) then
        return
    end

    if api.callbacks then
        api:UnregisterAllCallbacks(AccountType)
    else
        api.UnregisterAllCallbacks(AccountType)
    end
end

function AccountType.Initialize(owner)
    AccountType.owner = owner or AccountType.owner or MK

    local api = GetNetEaseAPI()
    if not api then
        return nil
    end

    if api.SetClientInfo then
        api:SetClientInfo({
            addonName = addonName,
            version = GetAddonVersion(),
        })
    end

    UnregisterNetEaseCallbacks(api)
    RegisterNetEaseCallback(api, "PlayerIdentityQueued")
    RegisterNetEaseCallback(api, "PlayerIdentityUpdated")
    RegisterNetEaseCallback(api, "PlayerIdentityBatchUpdated")
    RegisterNetEaseCallback(api, "PlayerIdentityFailed")
    RegisterNetEaseCallback(api, "ServerStatusChanged")

    return api
end

function AccountType.Start(owner)
    AccountType.Initialize(owner)

    local function startLatest()
        local api = AccountType.Initialize(owner)
        if api and api.Start then
            api:Start()
        end
    end

    startLatest()
    if C_Timer and C_Timer.After then
        C_Timer.After(0, startLatest)
    end
end

function AccountType.ClearCache()
    local api = GetNetEaseAPI()
    if not (api and api.ClearCache) then
        return 0
    end

    ClearServiceConnectionFailure()
    local stats = api.GetStats and api:GetStats() or nil
    local count = stats and ((tonumber(stats.cacheCount) or 0) + (tonumber(stats.failedCount) or 0)) or 0
    api:ClearCache()
    return count
end

function AccountType.GetStats()
    local api = GetNetEaseAPI()
    if api and api.GetStats then
        return api:GetStats()
    end
    return nil
end

function AccountType.SetTestIdentity(name, realm, identity)
    local fullName = BuildFullName(name, realm)
    if not (fullName and type(identity) == "table") then
        return false
    end

    AccountType.testIdentities = AccountType.testIdentities or {}
    identity.name = identity.name or fullName
    identity.updatedAt = identity.updatedAt or (time and time() or 0)
    AccountType.testIdentities[fullName] = identity
    return true
end

function AccountType.DebugQuery(name, realm, force)
    local api = GetNetEaseAPI()
    local fullName = BuildFullName(name, realm)
    if not (api and fullName) then
        return {
            leader = fullName or name,
            available = false,
            accountStatus = "unknown",
        }
    end

    local data, queryStatus, reason
    local serviceCooldownActive = IsServiceFailureCooldownActive()
    if force ~= false and not serviceCooldownActive and api.QueryPlayerIdentity then
        data, queryStatus, reason = api:QueryPlayerIdentity(fullName, true)
    else
        data = api.GetPlayerIdentity and api:GetPlayerIdentity(fullName) or nil
        if api.GetPlayerIdentityStatus then
            queryStatus, reason = api:GetPlayerIdentityStatus(fullName)
        end
        queryStatus = queryStatus or "missing"
    end

    local stats = GetAccountTypeStats(api)
    local serviceStatusForDebug, serviceMessageForDebug, serviceReasonForDebug = GetNetEaseServiceStatus(api, stats, fullName)
    local displayQueryStatus = queryStatus
    local queryFailureReason = reason or stats.unsupportedReason
    if type(data) ~= "table" then
        if serviceCooldownActive then
            displayQueryStatus, queryFailureReason = GetServiceFailureStatus()
            queryStatus = displayQueryStatus
        elseif reason == "not_started" then
            local serviceStatus, serviceReason = NoteServiceConnectionAttempt(api, stats, fullName)
            displayQueryStatus = serviceStatus or "starting"
            queryStatus = displayQueryStatus
            queryFailureReason = serviceReason or reason
        elseif reason == "waiting_connection" then
            local serviceStatus, serviceReason = NoteServiceConnectionAttempt(api, stats, fullName)
            displayQueryStatus = serviceStatus or "service_connection_missing"
            queryStatus = displayQueryStatus
            queryFailureReason = serviceReason or reason
        elseif queryStatus == "pending" then
            local serviceStatus, serviceReason = NoteServiceConnectionAttempt(api, stats, fullName)
            if serviceStatus then
                displayQueryStatus = serviceStatus
                queryFailureReason = queryFailureReason or serviceReason
                if serviceStatus == "service_unreachable"
                    or serviceStatus == "service_connection_missing"
                    or serviceStatus == "no_response"
                    or serviceStatus == "unavailable"
                    or serviceStatus == "starting" then
                    queryStatus = serviceStatus
                end
            end
        elseif queryStatus == "failed" then
            queryFailureReason = queryFailureReason or "timeout"
        elseif queryStatus == "missing" then
            local serviceStatus, serviceReason = NoteServiceConnectionAttempt(api, stats, fullName)
            if serviceStatus == "service_unreachable"
                or serviceStatus == "service_connection_missing"
                or serviceStatus == "no_response"
                or serviceStatus == "unavailable"
                or serviceStatus == "starting" then
                displayQueryStatus = serviceStatus
                queryStatus = serviceStatus
                queryFailureReason = serviceReason
            end
        end
    end

    local accountState = AccountType.GetAccountState(fullName, nil, {
        includeVeteran = true,
    })
    local connectionMessage = serviceMessageForDebug or stats.serviceMessage or stats.connectionMessage
    if stats.serviceStatus == "offline" or stats.connectionReason == "waiting_connection" then
        connectionMessage = stats.serviceMessage or ACCOUNT_TYPE_SERVICE_CONNECTION_MISSING_TEXT
    end

    return {
        leader = fullName,
        available = true,
        major = stats.major,
        minor = stats.minor,
        started = stats.started == true,
        connected = stats.connected == true,
        serviceConnected = stats.serviceConnected == true,
        serviceConnectionMissing = stats.serviceConnectionMissing == true,
        serviceStatus = serviceStatusForDebug or stats.serviceStatus,
        serviceMessage = serviceMessageForDebug or stats.serviceMessage,
        serviceReason = serviceReasonForDebug or stats.serviceReason,
        connectionStatus = stats.connectionStatus,
        connectionReason = stats.connectionReason,
        connectionMessage = connectionMessage,
        socketStatus = stats.socketStatus,
        socketStatusText = stats.socketStatusText,
        connectTarget = stats.connectTarget,
        target = stats.target,
        waitingSeconds = stats.waitingSeconds,
        cached = type(data) == "table",
        pending = displayQueryStatus == "pending" or displayQueryStatus == "connecting" or displayQueryStatus == "starting",
        queryStatus = queryStatus,
        displayQueryStatus = displayQueryStatus,
        queryFailureReason = queryFailureReason,
        serviceCooldownRemaining = GetServiceFailureCooldownRemaining(),
        accountStatus = accountState and accountState.status or "unknown",
        accountTypeInfo = accountState and accountState.accountTypeInfo or nil,
        cacheCount = stats.cacheCount,
        pendingCount = stats.pendingCount,
        failedCount = stats.failedCount,
    }
end
