local addonTable = select(2, ...)
---@type MKA
local MKA = addonTable.MKA
local core = addonTable
local TRANSLATE = core.TRANSLATE or function(msg) return msg end

core.MeetingStoneTab = core.MeetingStoneTab or {}
local MeetingStoneTab = core.MeetingStoneTab
local MeetingStoneBridge = core.MeetingStoneBridge or {}
local MeetingStoneListing = core.MeetingStoneListing or {}
local MeetingStoneForm = core.MeetingStoneForm or {}

local GetEditBoxText = MeetingStoneListing.GetEditBoxText
local SetMeetingStoneWidgetText = MeetingStoneListing.SetWidgetText
local SyncMeetingStoneNativeEntryCreationState = MeetingStoneListing.SyncNativeEntryCreationState
local CanManageMeetingStoneListing = MeetingStoneListing.CanManageListing
local HasMeetingStoneActiveListing = MeetingStoneListing.HasActiveListing
local IsMeetingStoneTextFilled = MeetingStoneListing.IsTextFilled
local IsMeetingStoneApplicantOnlyRefreshSource = MeetingStoneListing.IsApplicantOnlyRefreshSource
local RefreshMeetingStoneModernDropdown = MeetingStoneForm.RefreshDropdown

local MEETINGSTONE_READ_ONLY_ROLE_ATLAS_BY_KEY = core.GROUP_LIST_ROLE_ATLAS_BY_KEY or {
    TANK = "UI-LFG-RoleIcon-Tank",
    HEAL = "UI-LFG-RoleIcon-Healer",
    DPS = "UI-LFG-RoleIcon-DPS",
}
local MEETINGSTONE_READ_ONLY_ROLE_KEY_BY_ASSIGNED_ROLE = {
    TANK = "TANK",
    HEALER = "HEAL",
    HEAL = "HEAL",
    DAMAGER = "DPS",
    DPS = "DPS",
}

local function RestoreMeetingStoneNativeTextFromDraft(widgets, draft)
    if not (widgets and draft and widgets.meetingStoneUsesNativeTextControls) then
        return
    end

    local title = draft.title or ""
    if title ~= "" and widgets.meetingStoneTitleBox and GetEditBoxText(widgets.meetingStoneTitleBox) == "" then
        SetMeetingStoneWidgetText(widgets.meetingStoneTitleBox, title)
    end

    local summary = draft.summary or ""
    if summary ~= "" and widgets.meetingStoneSummaryBox and GetEditBoxText(widgets.meetingStoneSummaryBox) == "" then
        SetMeetingStoneWidgetText(widgets.meetingStoneSummaryBox, summary)
    end
end

local function ClearMeetingStoneReadOnlyDraft(owner)
    if not owner then
        return
    end

    owner.meetingStoneDraft = {
        title = "",
        summary = "",
        itemLevel = "0",
        score = "0",
    }
    owner.meetingStoneDraftID = (owner.meetingStoneDraftID or 0) + 1
end

local function GetMeetingStoneRequirementInputText(value, fallback)
    if value == nil then
        return fallback or "0"
    end
    return tostring(value)
end

local function ResetMeetingStoneUnavailableDraft(owner)
    if not owner then
        return
    end

    local draft = owner.meetingStoneDraft
    local needsReset = type(draft) ~= "table"
        or draft.activityID ~= nil
        or draft.mapID ~= nil
        or draft.activityName ~= nil
        or draft.dungeonName ~= nil
        or tostring(draft.title or "") ~= ""
        or tostring(draft.summary or "") ~= ""
        or tostring(draft.itemLevel or "0") ~= "0"
        or tostring(draft.score or "0") ~= "0"

    if needsReset then
        ClearMeetingStoneReadOnlyDraft(owner)
    end
end

local function UseMeetingStoneFallbackInputControls(widgets)
    if not widgets then
        return
    end

    if widgets.meetingStoneFallbackTitleBox then
        widgets.meetingStoneFallbackTitleBox:Show()
        widgets.meetingStoneTitleBox = widgets.meetingStoneFallbackTitleBox
    end
    if widgets.meetingStoneFallbackSummaryFrame then
        widgets.meetingStoneFallbackSummaryFrame:Show()
        widgets.meetingStoneSummaryFrame = widgets.meetingStoneFallbackSummaryFrame
        widgets.meetingStoneSummaryBox = widgets.meetingStoneFallbackSummaryBox
    end
    widgets.meetingStoneUsesNativeTextControls = false
end

local function ApplyMeetingStoneUnavailableState(owner, widgets)
    if not widgets then
        return
    end

    if owner and owner.DetachMeetingStoneNativeInputControls then
        owner:DetachMeetingStoneNativeInputControls()
    end

    UseMeetingStoneFallbackInputControls(widgets)
    ResetMeetingStoneUnavailableDraft(owner)
    local draft = owner and owner.meetingStoneDraft or {}

    SetMeetingStoneWidgetText(widgets.meetingStoneTitleBox, draft.title or "")
    SetMeetingStoneWidgetText(widgets.meetingStoneSummaryBox, draft.summary or "")
    SetMeetingStoneWidgetText(widgets.meetingStoneItemLevelBox, GetMeetingStoneRequirementInputText(draft.itemLevel, "0"))
    SetMeetingStoneWidgetText(widgets.meetingStoneScoreBox, GetMeetingStoneRequirementInputText(draft.score, "0"))
    widgets.meetingStoneDraftID = owner and owner.meetingStoneDraftID or widgets.meetingStoneDraftID

    if widgets.meetingStoneDungeonDropdown then
        RefreshMeetingStoneModernDropdown(widgets.meetingStoneDungeonDropdown)
    end
    if widgets.meetingStonePlayStyleDropdown then
        RefreshMeetingStoneModernDropdown(widgets.meetingStonePlayStyleDropdown)
    end
end

local function HasMeetingStoneActiveEntry()
    if HasMeetingStoneActiveListing then
        local ok, hasEntry = pcall(HasMeetingStoneActiveListing)
        return ok and hasEntry == true
    end
    if not (C_LFGList and C_LFGList.HasActiveEntryInfo) then
        return false
    end
    local ok, hasEntry = pcall(C_LFGList.HasActiveEntryInfo)
    return ok and hasEntry == true
end

local function SetMeetingStoneRegionShown(region, shown)
    if not region then
        return
    end
    if shown then
        region:Show()
    else
        region:Hide()
    end
end

local function RequestMeetingStoneListingSync(owner)
    if not (owner and owner.RequestGroupSync) then
        return
    end

    local now = GetTime and GetTime() or 0
    local previous = owner.mkaMeetingStoneListingSyncRequestAt or 0
    if now > 0 and (now - previous) < 1.5 then
        return
    end

    owner.mkaMeetingStoneListingSyncRequestAt = now
    owner:RequestGroupSync()
end

local function GetMeetingStoneReadOnlySharedDraft(owner, draft)
    if not (owner and owner.GetMeetingStoneSharedLeaderDraft) then
        return nil
    end

    local activityID = draft and draft.activityID
    local shared = owner:GetMeetingStoneSharedLeaderDraft(activityID)
    if type(shared) == "table" and shared.active ~= false and shared.activityID then
        return shared
    end
    return nil
end

local function HasRecentMeetingStoneSharedListingRemoval(owner)
    if not owner or not owner.meetingStoneSharedLeaderListingRemovedAt then
        return false
    end
    local now = GetTime and GetTime() or time and time() or 0
    if now <= 0 then
        return true
    end
    return (now - owner.meetingStoneSharedLeaderListingRemovedAt) < 8
end

local function ShouldShowMeetingStoneReadOnlyInfo(owner, draft, canManage, hasActiveEntry)
    if canManage or type(draft) ~= "table" then
        return false
    end

    if GetMeetingStoneReadOnlySharedDraft(owner, draft) then
        return true
    end
    if HasRecentMeetingStoneSharedListingRemoval(owner) then
        return false
    end

    return hasActiveEntry == true and draft.activityID ~= nil
end

local function GetMeetingStoneDisplayText(value, fallback)
    if IsMeetingStoneTextFilled and IsMeetingStoneTextFilled(value) then
        return tostring(value)
    end
    return fallback or TRANSLATE("MEETINGSTONE.SYNCING")
end

local function AddMeetingStoneRoleKey(roleKeys, roleKey)
    if not roleKey or #roleKeys >= 5 then
        return
    end
    roleKeys[#roleKeys + 1] = roleKey
end

local function AddMeetingStoneUnitAssignedRole(roleKeys, unit, seenGuids)
    if not (UnitExists and UnitExists(unit)) then
        return
    end

    if UnitGUID then
        local guid = UnitGUID(unit)
        if guid then
            if seenGuids[guid] then
                return
            end
            seenGuids[guid] = true
        end
    end

    local assignedRole = UnitGroupRolesAssigned and UnitGroupRolesAssigned(unit)
    AddMeetingStoneRoleKey(roleKeys, MEETINGSTONE_READ_ONLY_ROLE_KEY_BY_ASSIGNED_ROLE[assignedRole])
end

local function AddMeetingStoneCharacterEntryRole(owner, roleKeys, characterEntry, activeAliases)
    if not (owner and owner.NormalizeRoles and characterEntry and characterEntry.data) then
        return
    end
    if owner.IsCharacterEntryCurrentlyInGroup and not owner:IsCharacterEntryCurrentlyInGroup(characterEntry, activeAliases) then
        return
    end

    local roles = owner:NormalizeRoles(characterEntry.data)
    if roles.TANK then
        AddMeetingStoneRoleKey(roleKeys, "TANK")
    elseif roles.HEAL then
        AddMeetingStoneRoleKey(roleKeys, "HEAL")
    elseif roles.DPS then
        AddMeetingStoneRoleKey(roleKeys, "DPS")
    end
end

local function GetMeetingStoneReadOnlyGroupRoleKeys(owner)
    local roleKeys = {}
    local seenGuids = {}

    AddMeetingStoneUnitAssignedRole(roleKeys, "player", seenGuids)
    if IsInRaid and IsInRaid() then
        local count = GetNumGroupMembers and GetNumGroupMembers() or 0
        for index = 1, count do
            AddMeetingStoneUnitAssignedRole(roleKeys, "raid" .. index, seenGuids)
            if #roleKeys >= 5 then
                return roleKeys
            end
        end
    elseif IsInGroup and IsInGroup() then
        for index = 1, 4 do
            AddMeetingStoneUnitAssignedRole(roleKeys, "party" .. index, seenGuids)
            if #roleKeys >= 5 then
                return roleKeys
            end
        end
    end

    if #roleKeys == 0 and owner and owner.GetSharedGroupViewCharacters then
        local activeAliases = owner.GetActiveGroupCharacterAliases and owner:GetActiveGroupCharacterAliases() or nil
        for _, characterEntry in ipairs(owner:GetSharedGroupViewCharacters()) do
            AddMeetingStoneCharacterEntryRole(owner, roleKeys, characterEntry, activeAliases)
            if #roleKeys >= 5 then
                break
            end
        end
    end

    return roleKeys
end

local function RefreshMeetingStoneReadOnlyRoleSlots(owner, widgets)
    local roleKeys = GetMeetingStoneReadOnlyGroupRoleKeys(owner)
    for index, slot in ipairs(widgets.meetingStoneReadOnlyRoleSlots or {}) do
        local roleKey = roleKeys[index]
        local atlasName = MEETINGSTONE_READ_ONLY_ROLE_ATLAS_BY_KEY[roleKey]
        if atlasName and slot.icon then
            if slot.icon.SetAtlas then
                slot.icon:SetAtlas(atlasName, false)
            else
                slot.icon:SetTexture("Interface\\LFGFrame\\UI-LFG-ICON-ROLES")
            end
            slot.icon:SetVertexColor(1, 1, 1, 0.95)
            slot.icon:Show()
            if slot.empty then
                slot.empty:Hide()
            end
        else
            if slot.icon then
                slot.icon:Hide()
            end
            if slot.empty then
                slot.empty:Show()
            end
        end
    end
end

local function RefreshMeetingStoneReadOnlyInfo(owner, widgets, draft)
    if not widgets then
        return
    end

    draft = draft or {}
    local dungeonName = GetMeetingStoneDisplayText(draft.activityName or draft.dungeonName)
    local title = GetMeetingStoneDisplayText(draft.title)
    local itemLevel = GetMeetingStoneDisplayText(draft.itemLevel, "0")
    local score = GetMeetingStoneDisplayText(draft.score, "0")

    if widgets.meetingStoneReadOnlyDungeonText then
        widgets.meetingStoneReadOnlyDungeonText:SetText(dungeonName)
    end
    if widgets.meetingStoneReadOnlyTitleText then
        widgets.meetingStoneReadOnlyTitleText:SetText(title)
    end
    if widgets.meetingStoneReadOnlyItemLevelText then
        widgets.meetingStoneReadOnlyItemLevelText:SetText(itemLevel)
    end
    if widgets.meetingStoneReadOnlyScoreText then
        widgets.meetingStoneReadOnlyScoreText:SetText(score)
    end

    RefreshMeetingStoneReadOnlyRoleSlots(owner, widgets)
end

local function ApplyMeetingStoneLeftPanelMode(owner, widgets, draft, canManage, hasActiveEntry)
    local showReadOnlyInfo = ShouldShowMeetingStoneReadOnlyInfo(owner, draft, canManage, hasActiveEntry)

    for _, region in ipairs(widgets.meetingStoneFormWidgets or {}) do
        SetMeetingStoneRegionShown(region, not showReadOnlyInfo)
    end
    SetMeetingStoneRegionShown(widgets.meetingStoneReadOnlyInfoFrame, showReadOnlyInfo)

    if widgets.meetingStoneLeftHeaderTitle then
        widgets.meetingStoneLeftHeaderTitle:SetText(showReadOnlyInfo and TRANSLATE("MEETINGSTONE.FORM.INFO_TITLE") or TRANSLATE("MEETINGSTONE.FORM.MANAGE_TITLE"))
    end

    if showReadOnlyInfo then
        RefreshMeetingStoneReadOnlyInfo(owner, widgets, draft)
    end
end

local function GetMeetingStoneApplicantCount()
    if not (C_LFGList and C_LFGList.GetApplicants) then
        return 0
    end

    local ok, applicants = pcall(C_LFGList.GetApplicants)
    return ok and type(applicants) == "table" and #applicants or 0
end

function MKA:BuildMeetingStoneTabSnapshot()
    local managementAvailable = not self.IsMeetingStoneManagementAvailable or self:IsMeetingStoneManagementAvailable()
    local installed = managementAvailable and "1" or "0"
    local available = managementAvailable and "1" or "0"
    local loaded = managementAvailable and "1" or "0"
    local hasEntry = managementAvailable and HasMeetingStoneActiveEntry() and "1" or "0"
    local canManageListing = managementAvailable and (not CanManageMeetingStoneListing or CanManageMeetingStoneListing()) or false
    local canManage = canManageListing and "1" or "0"
    local draft = self.meetingStoneDraft or {}
    local applicantCount = managementAvailable and GetMeetingStoneApplicantCount() or 0
    return table.concat({
        "meetingstone",
        installed,
        available,
        loaded,
        hasEntry,
        canManage,
        tostring(draft.activityID or ""),
        tostring(draft.mapID or ""),
        tostring(draft.keyLevel or ""),
        tostring(draft.title or ""),
        tostring(draft.summary or ""),
        tostring(draft.itemLevel or ""),
        tostring(draft.score or ""),
        tostring(draft.playStyle or ""),
        tostring(applicantCount),
    }, "|")
end

function MKA:RefreshMeetingStoneTab(widgets, refreshSource)
    if not widgets then
        return
    end

    local meetingStoneAvailable = not self.IsMeetingStoneManagementAvailable or self:IsMeetingStoneManagementAvailable()
    if not meetingStoneAvailable then
        ApplyMeetingStoneUnavailableState(self, widgets)
        ApplyMeetingStoneLeftPanelMode(self, widgets, self.meetingStoneDraft, true, false)
        self:RefreshMeetingStoneActionButtons(widgets)
        if core.MeetingStoneApplicantList and core.MeetingStoneApplicantList.Refresh then
            core.MeetingStoneApplicantList.Refresh(self, widgets, refreshSource)
        end
        return
    end

    local canManage = not CanManageMeetingStoneListing or CanManageMeetingStoneListing()
    local hasActiveEntry = HasMeetingStoneActiveEntry()
    local draftTextDirty = canManage
        and self.HasMeetingStoneDraftTextDirty
        and self:HasMeetingStoneDraftTextDirty()
    local preserveManagedFormText = canManage
        and hasActiveEntry
        and (
            (IsMeetingStoneApplicantOnlyRefreshSource and IsMeetingStoneApplicantOnlyRefreshSource(refreshSource))
            or (draftTextDirty and refreshSource == "MeetingStoneActiveEntryUpdate")
        )
    if canManage then
        self:AttachMeetingStoneNativeInputControls(widgets)
    else
        if self.DetachMeetingStoneNativeInputControls then
            self:DetachMeetingStoneNativeInputControls()
        end
        UseMeetingStoneFallbackInputControls(widgets)
    end
    if not canManage then
        local restored = false
        local restoredFromNative = false
        if self.RestoreMeetingStoneReadOnlyDraftFromActiveEntry then
            restoredFromNative = self:RestoreMeetingStoneReadOnlyDraftFromActiveEntry(true) == true
            restored = restoredFromNative
        elseif self.RestoreMeetingStoneDraftFromActiveEntry then
            restoredFromNative = self:RestoreMeetingStoneDraftFromActiveEntry(true) == true
            restored = restoredFromNative
        end
        hasActiveEntry = hasActiveEntry or restoredFromNative
        local recentlyRemoved = not restoredFromNative and HasRecentMeetingStoneSharedListingRemoval(self)
        if not restored and not recentlyRemoved and self.RestoreMeetingStoneDraftFromSharedLeaderEntry then
            restored = self:RestoreMeetingStoneDraftFromSharedLeaderEntry()
        end
        if not restored then
            ClearMeetingStoneReadOnlyDraft(self)
        end
        local activeDraft = self.meetingStoneDraft or {}
        local sharedDraft = GetMeetingStoneReadOnlySharedDraft(self, activeDraft)
        local missingSharedText = not sharedDraft
            or not IsMeetingStoneTextFilled
            or not IsMeetingStoneTextFilled(sharedDraft.title)
        if missingSharedText and not restoredFromNative and not recentlyRemoved then
            RequestMeetingStoneListingSync(self)
        end
    else
        if not preserveManagedFormText then
            self:RestoreMeetingStoneDraftFromActiveEntry(hasActiveEntry == true)
        end
    end

    local draft = self.meetingStoneDraft
    if widgets.meetingStoneDungeonDropdown then
        RefreshMeetingStoneModernDropdown(widgets.meetingStoneDungeonDropdown)
    end
    if widgets.meetingStonePlayStyleDropdown then
        RefreshMeetingStoneModernDropdown(widgets.meetingStonePlayStyleDropdown)
    end

    if draft then
        if widgets.meetingStoneDraftID ~= self.meetingStoneDraftID then
            if draft.activityID and widgets.meetingStoneUsesNativeTextControls and not preserveManagedFormText then
                SyncMeetingStoneNativeEntryCreationState(draft, { generalPlaystyle = draft.playStyle }, hasActiveEntry == true, hasActiveEntry == true)
            end
            if not widgets.meetingStoneUsesNativeTextControls then
                SetMeetingStoneWidgetText(widgets.meetingStoneTitleBox, draft.title or "")
                SetMeetingStoneWidgetText(widgets.meetingStoneSummaryBox, draft.summary or "")
            end
            SetMeetingStoneWidgetText(widgets.meetingStoneItemLevelBox, GetMeetingStoneRequirementInputText(draft.itemLevel, "0"))
            SetMeetingStoneWidgetText(widgets.meetingStoneScoreBox, GetMeetingStoneRequirementInputText(draft.score, "0"))
            if widgets.meetingStonePrivateToggle then widgets.meetingStonePrivateToggle:SetChecked(draft.privateGroup == true) end
            widgets.meetingStoneDraftID = self.meetingStoneDraftID
        elseif not widgets.meetingStoneUsesNativeTextControls and widgets.meetingStoneTitleBox and GetEditBoxText(widgets.meetingStoneTitleBox) == "" then
            SetMeetingStoneWidgetText(widgets.meetingStoneTitleBox, draft.title or "")
        end
        RestoreMeetingStoneNativeTextFromDraft(widgets, draft)
    end

    ApplyMeetingStoneLeftPanelMode(self, widgets, draft, canManage, hasActiveEntry)
    self:RefreshMeetingStoneActionButtons(widgets)

    if core.MeetingStoneApplicantList and core.MeetingStoneApplicantList.Refresh then
        core.MeetingStoneApplicantList.Refresh(self, widgets, refreshSource)
    end
end

function MeetingStoneTab.Build(owner, tabGroup)
    if core.MeetingStoneLayout and core.MeetingStoneLayout.Build then
        return core.MeetingStoneLayout.Build(owner, tabGroup)
    end
end
