local _, addon = ...
local HLY = addon.HLY

local ICON_PATH = "Interface\\AddOns\\HLYqol\\Assets\\baguette.tga"
local SOUND_PATH = "Interface\\AddOns\\HLYqol\\Assets\\ohoho.mp3"
local WHITE_TEXTURE = "Interface\\Buttons\\WHITE8X8"
local FLAG_BLUE = { 0.0, 0.22, 0.65, 0.24 }
local FLAG_WHITE = { 1.0, 1.0, 1.0, 0.16 }
local FLAG_RED = { 0.9, 0.16, 0.22, 0.24 }
local SOLID_RED = { 0.9, 0.16, 0.22, 0.95 }

local knownApplicants = {}

local function SafeCall(func, ...)
    if not func then
        return nil
    end

    local ok, result = pcall(func, ...)
    if ok then
        return result
    end

    return nil
end

local function SafeNumber(value)
    local ok, numberValue = pcall(tonumber, value)
    if ok and numberValue then
        local usable = pcall(function()
            return numberValue >= 0 or numberValue < 0
        end)
        if usable then
            return numberValue
        end
    end

    return 0
end

local function UTF8(...)
    return string.char(...)
end

local ACCENT_REPLACEMENTS = {
    [UTF8(195, 160)] = "a",
    [UTF8(195, 162)] = "a",
    [UTF8(195, 164)] = "a",
    [UTF8(195, 161)] = "a",
    [UTF8(195, 167)] = "c",
    [UTF8(195, 169)] = "e",
    [UTF8(195, 168)] = "e",
    [UTF8(195, 170)] = "e",
    [UTF8(195, 171)] = "e",
    [UTF8(195, 174)] = "i",
    [UTF8(195, 175)] = "i",
    [UTF8(195, 172)] = "i",
    [UTF8(195, 173)] = "i",
    [UTF8(195, 180)] = "o",
    [UTF8(195, 182)] = "o",
    [UTF8(195, 178)] = "o",
    [UTF8(195, 179)] = "o",
    [UTF8(195, 185)] = "u",
    [UTF8(195, 187)] = "u",
    [UTF8(195, 188)] = "u",
    [UTF8(195, 186)] = "u",
    [UTF8(195, 191)] = "y",
    [UTF8(197, 147)] = "oe",
    [UTF8(195, 166)] = "ae",
}

local FRENCH_REALMS = {
    arakarahm = true,
    archimonde = true,
    chantseternels = true,
    chogall = true,
    confrerieduthorium = true,
    conseildesombres = true,
    cultedelarivenoire = true,
    dalaran = true,
    drekthar = true,
    eldrethalas = true,
    eitrigg = true,
    elune = true,
    garona = true,
    hyjal = true,
    illidan = true,
    kaelthas = true,
    khazmodan = true,
    kirintor = true,
    krasus = true,
    lacroisadeecarlate = true,
    lesclairvoyants = true,
    lessentinelles = true,
    marecagedezangar = true,
    medivh = true,
    naxxramas = true,
    nerzhul = true,
    rashgarroth = true,
    sargeras = true,
    sinstralis = true,
    suramar = true,
    templenoir = true,
    throkferoth = true,
    uldaman = true,
    varimathras = true,
    voljin = true,
    ysondre = true,
}

local function IsEnabled()
    return HLY.db and HLY.db.frenchConnection and HLY.db.frenchConnection.enabled
end

local function IsGroupFinderLoaded()
    if C_AddOns and C_AddOns.IsAddOnLoaded then
        return C_AddOns.IsAddOnLoaded("Blizzard_GroupFinder")
    end

    if IsAddOnLoaded then
        return IsAddOnLoaded("Blizzard_GroupFinder")
    end

    return LFGListFrame ~= nil
end

local function NormalizeRealmName(realmName)
    if type(realmName) ~= "string" or realmName == "" then
        return nil
    end

    local normalized = realmName:lower()
    for accented, replacement in pairs(ACCENT_REPLACEMENTS) do
        normalized = normalized:gsub(accented, replacement)
    end

    return normalized:gsub("[%s%-'%._]", "")
end

local function ExtractRealmFromFullName(fullName)
    if type(fullName) ~= "string" or fullName == "" then
        return nil
    end

    fullName = fullName:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", "")
    local realmName = fullName:match("^[^-]+%-(.+)$")
    if not realmName or realmName == "" then
        return nil
    end

    return realmName
end

local function IsFrenchRealmPlayer(fullName)
    local ok, result = pcall(function()
        local realmName = ExtractRealmFromFullName(fullName)
        local normalizedRealm = NormalizeRealmName(realmName)
        return normalizedRealm and FRENCH_REALMS[normalizedRealm] or false
    end)

    return ok and result or false
end

local function EnsureMarkerTexture(parent, key, layer, subLevel, color)
    if not parent then
        return nil
    end

    local texture = parent[key]
    if not texture then
        texture = parent:CreateTexture(nil, layer or "BACKGROUND", nil, subLevel or 0)
        texture:SetTexture(WHITE_TEXTURE)
        parent[key] = texture
    end

    texture:SetColorTexture(color[1], color[2], color[3], color[4])
    return texture
end

local function EnsureIconTexture(parent, key, width, height)
    if not parent then
        return nil
    end

    local texture = parent[key]
    if not texture then
        texture = parent:CreateTexture(nil, "ARTWORK", nil, 5)
        texture:SetTexture(ICON_PATH)
        texture:SetBlendMode("BLEND")
        parent[key] = texture
    end

    texture:SetSize(width, height)
    return texture
end

local function LayoutApplicantFlag(member, blue, white, red)
    local totalWidth = math.max(1, math.floor(member:GetWidth() - 4))
    local firstThird = math.floor(totalWidth / 3)
    local secondThird = math.floor((totalWidth * 2) / 3)

    blue:ClearAllPoints()
    blue:SetPoint("TOPLEFT", member, "TOPLEFT", 4, 1)
    blue:SetPoint("BOTTOMLEFT", member, "BOTTOMLEFT", 4, -1)
    blue:SetWidth(firstThird)

    white:ClearAllPoints()
    white:SetPoint("TOPLEFT", blue, "TOPRIGHT", 0, 0)
    white:SetPoint("BOTTOMLEFT", blue, "BOTTOMRIGHT", 0, 0)
    white:SetWidth(secondThird - firstThird)

    red:ClearAllPoints()
    red:SetPoint("TOPLEFT", white, "TOPRIGHT", 0, 0)
    red:SetPoint("BOTTOMLEFT", white, "BOTTOMRIGHT", 0, 0)
    red:SetPoint("TOPRIGHT", member, "TOPRIGHT", 1, 1)
    red:SetPoint("BOTTOMRIGHT", member, "BOTTOMRIGHT", 1, -1)
end

local function PositionApplicantDecor(member, icon)
    local leaverShown = member.LeaverIcon and member.LeaverIcon:IsShown()
    local friendShown = member.FriendIcon and member.FriendIcon:IsShown()

    icon:ClearAllPoints()
    member.Name:ClearAllPoints()

    if leaverShown and friendShown then
        member.FriendIcon:SetPoint("LEFT", member.LeaverIcon, "RIGHT")
        icon:SetPoint("LEFT", member.FriendIcon, "RIGHT", 1, 0)
    elseif leaverShown then
        icon:SetPoint("LEFT", member.LeaverIcon, "RIGHT", 1, 0)
    elseif friendShown then
        member.FriendIcon:SetPoint("LEFT", 7, 0)
        icon:SetPoint("LEFT", member.FriendIcon, "RIGHT", 1, 0)
    else
        icon:SetPoint("LEFT", 7, 0)
    end

    member.Name:SetPoint("LEFT", icon, "RIGHT", 3, 0)
    member.Name:SetPoint("RIGHT", member, "LEFT", 95, 0)
end

local function UpdateApplicantDecor(member, isFrench)
    if not member then
        return
    end

    local blue = EnsureMarkerTexture(member, "__HLYFrenchBlue", "BACKGROUND", -7, FLAG_BLUE)
    local white = EnsureMarkerTexture(member, "__HLYFrenchWhite", "BACKGROUND", -7, FLAG_WHITE)
    local red = EnsureMarkerTexture(member, "__HLYFrenchRed", "BACKGROUND", -7, FLAG_RED)
    local marker = EnsureMarkerTexture(member, "__HLYFrenchMarker", "ARTWORK", 1, SOLID_RED)
    local icon = EnsureIconTexture(member, "__HLYFrenchIcon", 20, 11)
    local showBaguette = HLY.db.frenchConnection.showBaguette

    LayoutApplicantFlag(member, blue, white, red)
    marker:ClearAllPoints()
    marker:SetPoint("TOPLEFT", member, "TOPLEFT", 0, 1)
    marker:SetPoint("BOTTOMLEFT", member, "BOTTOMLEFT", 0, -1)
    marker:SetWidth(4)

    if isFrench and IsEnabled() then
        if showBaguette and icon then
            PositionApplicantDecor(member, icon)
            icon:Show()
        elseif icon then
            icon:Hide()
            member.Name:ClearAllPoints()
            member.Name:SetPoint("LEFT", 7, 0)
            member.Name:SetPoint("RIGHT", member, "LEFT", 95, 0)
        end

        blue:Show()
        white:Show()
        red:Show()
        marker:Show()
    else
        if icon then
            icon:Hide()
        end
        blue:Hide()
        white:Hide()
        red:Hide()
        marker:Hide()
    end
end

local function UpdateSearchEntryDecor(entry, isFrench)
    if not entry then
        return
    end

    local blue = EnsureMarkerTexture(entry, "__HLYFrenchBlue", "ARTWORK", -1, FLAG_BLUE)
    local white = EnsureMarkerTexture(entry, "__HLYFrenchWhite", "ARTWORK", -1, FLAG_WHITE)
    local red = EnsureMarkerTexture(entry, "__HLYFrenchRed", "ARTWORK", -1, FLAG_RED)
    local marker = EnsureMarkerTexture(entry, "__HLYFrenchMarker", "ARTWORK", 1, SOLID_RED)
    local icon = EnsureIconTexture(entry, "__HLYFrenchIcon", 24, 13)
    local showBaguette = HLY.db.frenchConnection.showBaguette

    blue:ClearAllPoints()
    blue:SetPoint("TOPLEFT", entry, "TOPLEFT", 6, -1)
    blue:SetPoint("BOTTOMLEFT", entry, "BOTTOMLEFT", 6, -2)
    blue:SetPoint("RIGHT", entry, "LEFT", 106, 0)

    white:ClearAllPoints()
    white:SetPoint("TOPLEFT", blue, "TOPRIGHT", 0, 0)
    white:SetPoint("BOTTOMLEFT", blue, "BOTTOMRIGHT", 0, 0)
    white:SetPoint("RIGHT", entry, "LEFT", 207, 0)

    red:ClearAllPoints()
    red:SetPoint("TOPLEFT", white, "TOPRIGHT", 0, 0)
    red:SetPoint("BOTTOMLEFT", white, "BOTTOMRIGHT", 0, 0)
    red:SetPoint("TOPRIGHT", entry, "TOPRIGHT", -2, -1)
    red:SetPoint("BOTTOMRIGHT", entry, "BOTTOMRIGHT", -2, -2)

    marker:ClearAllPoints()
    marker:SetPoint("TOPLEFT", entry, "TOPLEFT", 2, -1)
    marker:SetPoint("BOTTOMLEFT", entry, "BOTTOMLEFT", 2, -2)
    marker:SetWidth(4)

    if isFrench and IsEnabled() then
        if showBaguette and icon then
            entry.Name:ClearAllPoints()
            entry.Name:SetPoint("TOPLEFT", entry, "TOPLEFT", 38, -6)
            icon:ClearAllPoints()
            icon:SetPoint("LEFT", entry, "LEFT", 10, 0)
            icon:Show()
        else
            if icon then
                icon:Hide()
            end
            entry.Name:ClearAllPoints()
            entry.Name:SetPoint("TOPLEFT", entry, "TOPLEFT", 10, -6)
        end

        blue:Show()
        white:Show()
        red:Show()
        marker:Show()
    else
        if icon then
            icon:Hide()
        end
        entry.Name:ClearAllPoints()
        entry.Name:SetPoint("TOPLEFT", entry, "TOPLEFT", 10, -6)
        blue:Hide()
        white:Hide()
        red:Hide()
        marker:Hide()
    end
end

local function ApplicantHasFrenchMember(applicantID)
    local info = SafeCall(C_LFGList.GetApplicantInfo, applicantID)
    if not info or not info.numMembers then
        return false
    end

    for memberIdx = 1, SafeNumber(info.numMembers) do
        local fullName = SafeCall(C_LFGList.GetApplicantMemberInfo, applicantID, memberIdx)
        if IsFrenchRealmPlayer(fullName) then
            return true
        end
    end

    return false
end

function HLY:UpdateFrenchApplicantSounds()
    if not IsEnabled() or not self.db.frenchConnection.playSound or not C_LFGList or not C_LFGList.GetApplicants then
        return
    end

    local currentApplicants = {}
    local applicants = SafeCall(C_LFGList.GetApplicants) or {}

    for _, applicantID in ipairs(applicants) do
        currentApplicants[applicantID] = true

        if not knownApplicants[applicantID] then
            knownApplicants[applicantID] = true
            if ApplicantHasFrenchMember(applicantID) then
                PlaySoundFile(SOUND_PATH, "Master")
            end
        end
    end

    for applicantID in pairs(knownApplicants) do
        if not currentApplicants[applicantID] then
            knownApplicants[applicantID] = nil
        end
    end
end

function HLY:RefreshFrenchConnection()
    if not IsGroupFinderLoaded() then
        return
    end

    if LFGListFrame and LFGListFrame.ApplicationViewer and LFGListFrame.ApplicationViewer.ScrollBox then
        local scrollTarget = LFGListFrame.ApplicationViewer.ScrollBox.ScrollTarget
        if scrollTarget and scrollTarget.GetChildren then
            for _, child in ipairs({ scrollTarget:GetChildren() }) do
                if child and child.applicantID and child.Members then
                    for _, member in ipairs(child.Members) do
                        if member and member.memberIdx then
                            local fullName = SafeCall(C_LFGList.GetApplicantMemberInfo, child.applicantID, member.memberIdx)
                            UpdateApplicantDecor(member, IsFrenchRealmPlayer(fullName))
                        end
                    end
                end
            end
        end
    end

    if LFGListFrame and LFGListFrame.SearchPanel and LFGListFrame.SearchPanel.ScrollBox then
        local scrollTarget = LFGListFrame.SearchPanel.ScrollBox.ScrollTarget
        if scrollTarget and scrollTarget.GetChildren then
            for _, child in ipairs({ scrollTarget:GetChildren() }) do
                if child and child.resultID and SafeCall(C_LFGList.HasSearchResultInfo, child.resultID) then
                    local searchResultInfo = SafeCall(C_LFGList.GetSearchResultInfo, child.resultID)
                    UpdateSearchEntryDecor(child, searchResultInfo and IsFrenchRealmPlayer(searchResultInfo.leaderName))
                end
            end
        end
    end
end

function HLY:InstallFrenchConnectionHooks()
    if self.frenchConnectionHooksInstalled or not IsGroupFinderLoaded() then
        return
    end

    hooksecurefunc("LFGListApplicationViewer_UpdateApplicantMember", function(member, applicantID, memberIdx)
        local fullName = SafeCall(C_LFGList.GetApplicantMemberInfo, applicantID, memberIdx)
        UpdateApplicantDecor(member, IsFrenchRealmPlayer(fullName))
    end)

    hooksecurefunc("LFGListSearchEntry_Update", function(entry)
        if not entry or not entry.resultID or not SafeCall(C_LFGList.HasSearchResultInfo, entry.resultID) then
            return
        end

        local searchResultInfo = SafeCall(C_LFGList.GetSearchResultInfo, entry.resultID)
        UpdateSearchEntryDecor(entry, searchResultInfo and IsFrenchRealmPlayer(searchResultInfo.leaderName))
    end)

    self.frenchConnectionHooksInstalled = true
    self:RefreshFrenchConnection()
end

function HLY:HandleFrenchConnectionAddonLoaded(_, loadedAddon)
    if loadedAddon == "Blizzard_GroupFinder" then
        self:InstallFrenchConnectionHooks()
    end
end

function HLY:SetupFrenchConnection()
    if self.frenchConnectionSetup then
        self:InstallFrenchConnectionHooks()
        return
    end

    self:RegisterEvent("ADDON_LOADED", "HandleFrenchConnectionAddonLoaded")
    self:RegisterEvent("LFG_LIST_APPLICANT_LIST_UPDATED", "UpdateFrenchApplicantSounds")
    self:InstallFrenchConnectionHooks()
    self.frenchConnectionSetup = true
end
