local _, addonTable = ...
---@type MK
local MK = addonTable.MK
local core = addonTable
local TRANSLATE = core.TRANSLATE or function(msg) return msg end

local BLACKLIST_NOTE_ADVERTISEMENT = "广告"
local BLACKLIST_NOTE_SAME_TITLE_AD = "同标题广告传染"
local BLACKLIST_NOTE_MANUAL = "手动拉黑"

local function GetNow()
    if time then
        return time()
    end
    return GetTime and math.floor(GetTime()) or 0
end

local function TrimString(value)
    if type(value) ~= "string" then
        return nil
    end
    if strtrim then
        value = strtrim(value)
    else
        value = value:match("^%s*(.-)%s*$") or value
    end
    if value == "" then
        return nil
    end
    return value
end

local function NormalizeIdentifierPart(value)
    value = TrimString(value)
    if not value then
        return nil
    end
    return string.lower(value:gsub("%s+", ""))
end

local function GetCurrentRealmName()
    local realm = GetNormalizedRealmName and GetNormalizedRealmName()
    if realm and realm ~= "" then
        return realm
    end
    realm = GetRealmName and GetRealmName()
    if realm and realm ~= "" then
        return realm
    end
    return nil
end

local function SplitPlayerName(playerName, realm)
    playerName = TrimString(playerName)
    realm = TrimString(realm)
    if not playerName then
        return nil, nil
    end

    if playerName:find("-", 1, true) then
        local namePart, realmPart = strsplit("-", playerName)
        playerName = TrimString(namePart) or playerName
        realm = TrimString(realmPart) or realm
    end

    if not realm then
        realm = GetCurrentRealmName()
    end

    return playerName, realm
end

local function BuildDisplayName(name, realm)
    if realm and realm ~= "" then
        return string.format("%s-%s", name, realm)
    end
    return name
end

local function BuildBlacklistKey(name, realm)
    local normalizedName = NormalizeIdentifierPart(name)
    if not normalizedName then
        return nil
    end
    local normalizedRealm = NormalizeIdentifierPart(realm)
    if normalizedRealm then
        return normalizedName .. "-" .. normalizedRealm
    end
    return normalizedName
end

local function NamesCanMatch(leftName, leftRealm, rightName, rightRealm)
    local normalizedLeftName = NormalizeIdentifierPart(leftName)
    local normalizedRightName = NormalizeIdentifierPart(rightName)
    if not normalizedLeftName or normalizedLeftName ~= normalizedRightName then
        return false
    end

    local normalizedLeftRealm = NormalizeIdentifierPart(leftRealm)
    local normalizedRightRealm = NormalizeIdentifierPart(rightRealm)
    if normalizedLeftRealm and normalizedRightRealm and normalizedLeftRealm ~= normalizedRightRealm then
        return false
    end

    return true
end

local function CopyBlacklistRecord(record)
    if type(record) ~= "table" then
        return record
    end

    local copy = {}
    for key, value in pairs(record) do
        copy[key] = value
    end
    return copy
end

local function GetCurrentProfileMigrationKey(db)
    if type(db) ~= "table" then
        return "__default"
    end
    if type(db.GetCurrentProfile) == "function" then
        local ok, profileName = pcall(db.GetCurrentProfile, db)
        if ok and profileName and profileName ~= "" then
            return tostring(profileName)
        end
    end
    return "__default"
end

local function MigrateProfileBlacklist(owner, blacklist)
    if not (owner and owner.db and type(blacklist) == "table" and type(blacklist.players) == "table") then
        return
    end

    blacklist.migratedProfiles = type(blacklist.migratedProfiles) == "table" and blacklist.migratedProfiles or {}
    local migrationKey = GetCurrentProfileMigrationKey(owner.db)
    if blacklist.migratedProfiles[migrationKey] then
        return
    end

    local profile = owner.db.profile
    local profileBlacklist = type(profile) == "table" and profile.blacklist or nil
    local profilePlayers = type(profileBlacklist) == "table" and profileBlacklist.players or nil
    if type(profilePlayers) == "table" then
        for key, record in pairs(profilePlayers) do
            if blacklist.players[key] == nil then
                blacklist.players[key] = CopyBlacklistRecord(record)
            end
        end
    end

    blacklist.migratedProfiles[migrationKey] = true
end

local function GetBlacklistStorage(owner)
    local db = owner and owner.db
    if type(db) ~= "table" then
        return nil
    end

    db.global = type(db.global) == "table" and db.global or {}
    db.global.blacklist = type(db.global.blacklist) == "table" and db.global.blacklist or {}
    db.global.blacklist.players = type(db.global.blacklist.players) == "table" and db.global.blacklist.players or {}
    MigrateProfileBlacklist(owner, db.global.blacklist)
    return db.global.blacklist.players
end

local function FindBlacklistRecordKey(players, playerName, realm)
    if type(players) ~= "table" then
        return nil
    end

    local namePart, realmPart = SplitPlayerName(playerName, realm)
    local exactKey = BuildBlacklistKey(namePart, realmPart)
    if exactKey and players[exactKey] ~= nil then
        return exactKey
    end

    local shortKey = BuildBlacklistKey(namePart)
    if shortKey and players[shortKey] ~= nil then
        return shortKey
    end

    for key, record in pairs(players) do
        if type(record) == "table" then
            local recordName = record.name or record.displayName or key
            local recordRealm = record.realm
            if NamesCanMatch(recordName, recordRealm, namePart, realmPart) then
                return key
            end
        end
    end

    return nil
end

local function NormalizeBlacklistEntry(playerName, realm)
    local namePart, realmPart = SplitPlayerName(playerName, realm)
    local key = BuildBlacklistKey(namePart, realmPart)
    if not key then
        return nil
    end

    return {
        key = key,
        name = namePart,
        realm = realmPart,
        displayName = BuildDisplayName(namePart, realmPart),
    }
end

function MK:GetBlacklistStorage()
    return GetBlacklistStorage(self)
end

function MK:GetBlacklistNoteAdvertisement()
    return BLACKLIST_NOTE_ADVERTISEMENT
end

function MK:GetBlacklistNoteSameTitleAdvertisement()
    return BLACKLIST_NOTE_SAME_TITLE_AD
end

function MK:GetBlacklistNoteManual()
    return BLACKLIST_NOTE_MANUAL
end

function MK:NormalizeBlacklistPlayerName(playerName, realm)
    return NormalizeBlacklistEntry(playerName, realm)
end

function MK:IsPlayerBlacklisted(playerName, realm)
    local players = GetBlacklistStorage(self)
    local key = FindBlacklistRecordKey(players, playerName, realm)
    if key then
        return true, players[key], key
    end
    return false, nil, nil
end

function MK:AddPlayerToBlacklist(playerName, realm, note, source, forceNote)
    local players = GetBlacklistStorage(self)
    if not players then
        return false
    end

    local entry = NormalizeBlacklistEntry(playerName, realm)
    if not entry then
        return false
    end

    local existingKey = FindBlacklistRecordKey(players, entry.name, entry.realm)
    local key = existingKey or entry.key
    local now = GetNow()
    local record = type(players[key]) == "table" and players[key] or {}
    local previousNote = TrimString(record.note or "")
    local previousSource = record.source
    local nextNote = TrimString(note or "")

    record.key = key
    record.name = entry.name
    record.realm = entry.realm
    record.displayName = entry.displayName
    record.source = source or record.source
    record.addedAt = tonumber(record.addedAt) or now
    record.updatedAt = now

    if nextNote and (forceNote == true or not previousNote or previousNote == "" or previousSource == source) then
        record.note = nextNote
    else
        record.note = record.note or ""
    end

    players[key] = record

    if existingKey and existingKey ~= entry.key and players[entry.key] == record then
        players[entry.key] = nil
    end

    if self.RefreshMainWindow then
        self:RefreshMainWindow("BlacklistChanged", key)
    end

    return true, record, key
end

function MK:SetBlacklistNote(key, note)
    local players = GetBlacklistStorage(self)
    if not (players and key and players[key]) then
        return false
    end

    local record = players[key]
    record.note = tostring(note or "")
    record.updatedAt = GetNow()
    if self.RefreshMainWindow then
        self:RefreshMainWindow("BlacklistNoteChanged", key)
    end
    return true
end

function MK:RemovePlayerFromBlacklist(keyOrName, realm)
    local players = GetBlacklistStorage(self)
    if not players then
        return false
    end

    local key = players[keyOrName] and keyOrName or FindBlacklistRecordKey(players, keyOrName, realm)
    if not key then
        return false
    end

    local record = players[key]
    players[key] = nil
    if core.FindGroupSearch and core.FindGroupSearch.ClearBlacklistLinkedFilters then
        core.FindGroupSearch.ClearBlacklistLinkedFilters(self, record)
    end
    if self.RefreshMainWindow then
        self:RefreshMainWindow("BlacklistChanged", key)
    end
    return true, record, key
end

function MK:GetBlacklistEntries()
    local players = GetBlacklistStorage(self)
    local entries = {}
    for key, record in pairs(players or {}) do
        if type(record) == "table" then
            entries[#entries + 1] = {
                key = key,
                name = record.name or record.displayName or key,
                realm = record.realm,
                displayName = record.displayName or BuildDisplayName(record.name or key, record.realm),
                note = record.note or "",
                source = record.source or "",
                addedAt = tonumber(record.addedAt) or 0,
                updatedAt = tonumber(record.updatedAt) or 0,
            }
        end
    end

    table.sort(entries, function(left, right)
        local leftUpdated = tonumber(left.updatedAt) or 0
        local rightUpdated = tonumber(right.updatedAt) or 0
        if leftUpdated ~= rightUpdated then
            return leftUpdated > rightUpdated
        end
        return tostring(left.displayName or left.key) < tostring(right.displayName or right.key)
    end)

    return entries
end

function MK:GetBlacklistCount()
    local count = 0
    local players = GetBlacklistStorage(self)
    for _ in pairs(players or {}) do
        count = count + 1
    end
    return count
end

function MK:PrintBlacklistAdded(record)
    if self.Print and record then
        self:Print(string.format(TRANSLATE("%s 已加入黑名单。"), record.displayName or record.name or ""))
    end
end

function MK:PrintBlacklistRemoved(record)
    if self.Print and record then
        self:Print(string.format(TRANSLATE("%s 已从黑名单删除。"), record.displayName or record.name or ""))
    end
end
