-- LidCouncil_StartupHealth.lua
-- Read-only startup contract checks for release diagnostics.

local EnsureApiRegistry = rawget(_G, "LIDCOUNCIL_EnsureApiRegistry")
local API, INTERNAL_API
if type(EnsureApiRegistry) == "function" then
  API, INTERNAL_API = EnsureApiRegistry()
else
  API = rawget(_G, "LIDCOUNCIL_API")
  if type(API) ~= "table" then
    API = {}
    _G.LIDCOUNCIL_API = API
  end
  if type(API.__internal) ~= "table" then
    API.__internal = {}
  end
  INTERNAL_API = API.__internal
end

local MAX_CHECKS = 32
local MAX_PROBLEMS = 12
local MAX_DETAIL_LENGTH = 180

local function BoundText(value, limit)
  local text = tostring(value or "")
  local maxLength = math.max(1, tonumber(limit) or MAX_DETAIL_LENGTH)
  if #text > maxLength then
    return string.sub(text, 1, maxLength)
  end
  return text
end

local function CopyReport(report)
  report = type(report) == "table" and report or {}
  local copy = {
    version = math.max(1, tonumber(report.version) or 1),
    ran = report.ran == true,
    ready = report.ready == true,
    severity = BoundText(report.severity, 16),
    checkedAt = math.max(0, tonumber(report.checkedAt) or 0),
    checkCount = math.max(0, tonumber(report.checkCount) or 0),
    passedCount = math.max(0, tonumber(report.passedCount) or 0),
    warningCount = math.max(0, tonumber(report.warningCount) or 0),
    errorCount = math.max(0, tonumber(report.errorCount) or 0),
    checks = {},
    problems = {},
  }
  for index, check in ipairs(type(report.checks) == "table" and report.checks or {}) do
    if index > MAX_CHECKS then
      break
    end
    copy.checks[#copy.checks + 1] = {
      key = BoundText(type(check) == "table" and check.key or "", 64),
      label = BoundText(type(check) == "table" and check.label or "", 96),
      passed = type(check) == "table" and check.passed == true,
      severity = BoundText(type(check) == "table" and check.severity or "", 16),
      detail = BoundText(type(check) == "table" and check.detail or "", MAX_DETAIL_LENGTH),
    }
  end
  for index, problem in ipairs(type(report.problems) == "table" and report.problems or {}) do
    if index > MAX_PROBLEMS then
      break
    end
    copy.problems[#copy.problems + 1] = BoundText(problem, MAX_DETAIL_LENGTH)
  end
  return copy
end

INTERNAL_API.LIDCOUNCIL_CreateStartupHealthHelpers_Impl = function(deps)
  deps = type(deps) == "table" and deps or {}
  local BuildChecks = deps.BuildChecks
  local GetNow = deps.GetNow
  local OnCompleted = deps.OnCompleted
  local report = {
    version = 1,
    ran = false,
    ready = false,
    severity = "pending",
    checkedAt = 0,
    checkCount = 0,
    passedCount = 0,
    warningCount = 0,
    errorCount = 0,
    checks = {},
    problems = {},
  }

  local function AddResult(definition, passed, detail)
    definition = type(definition) == "table" and definition or {}
    local failedSeverity = definition.severity == "warning" and "warning" or "error"
    local result = {
      key = BoundText(definition.key, 64),
      label = BoundText(definition.label, 96),
      passed = passed == true,
      severity = passed == true and "healthy" or failedSeverity,
      detail = BoundText(detail, MAX_DETAIL_LENGTH),
    }
    report.checks[#report.checks + 1] = result
    report.checkCount = report.checkCount + 1
    if result.passed then
      report.passedCount = report.passedCount + 1
      return
    end
    if failedSeverity == "warning" then
      report.warningCount = report.warningCount + 1
    else
      report.errorCount = report.errorCount + 1
    end
    if #report.problems < MAX_PROBLEMS then
      local problem = result.label ~= "" and result.label or result.key
      if result.detail ~= "" then
        problem = problem .. ": " .. result.detail
      end
      report.problems[#report.problems + 1] = BoundText(problem, MAX_DETAIL_LENGTH)
    end
  end

  local function Run(force)
    if report.ran and force ~= true then
      return CopyReport(report)
    end
    report.ran = true
    report.ready = false
    report.severity = "pending"
    report.checkedAt = type(GetNow) == "function" and math.max(0, tonumber(GetNow()) or 0) or 0
    report.checkCount = 0
    report.passedCount = 0
    report.warningCount = 0
    report.errorCount = 0
    report.checks = {}
    report.problems = {}

    local built, definitions = pcall(type(BuildChecks) == "function" and BuildChecks or function()
      return {}
    end)
    if not built or type(definitions) ~= "table" then
      AddResult({
        key = "health_definition_builder",
        label = "Startup health definition builder",
      }, false, built and "invalid_check_list" or definitions)
    else
      for index, definition in ipairs(definitions) do
        if index > MAX_CHECKS then
          AddResult({
            key = "health_check_limit",
            label = "Startup health check limit",
            severity = "warning",
          }, false, "check list truncated at " .. tostring(MAX_CHECKS))
          break
        end
        local probe = type(definition) == "table" and definition.probe or nil
        local probeOk, passed, detail = pcall(type(probe) == "function" and probe or function()
          return false, "probe_unavailable"
        end)
        if not probeOk then
          detail = passed
          passed = false
        end
        AddResult(definition, passed, detail)
      end
    end

    report.ready = report.errorCount == 0
    if report.errorCount > 0 then
      report.severity = "error"
    elseif report.warningCount > 0 then
      report.severity = "warning"
    else
      report.severity = "healthy"
    end
    local snapshot = CopyReport(report)
    if type(OnCompleted) == "function" then
      pcall(OnCompleted, snapshot)
    end
    return snapshot
  end

  return {
    Run = Run,
    GetStatus = function()
      return CopyReport(report)
    end,
  }
end

local function BuildFunctionCheck(registry, name, label, severity)
  return {
    key = string.lower(name),
    label = label,
    severity = severity,
    probe = function()
      local value = type(registry) == "table" and registry[name] or nil
      return type(value) == "function", "resolved type " .. type(value)
    end,
  }
end

local function ReadAddonMetadata(field)
  local addonName = type(API.LIDCOUNCIL_ADDON_NAME) == "string" and API.LIDCOUNCIL_ADDON_NAME or "LidCouncil"
  if type(C_AddOns) == "table" and type(C_AddOns.GetAddOnMetadata) == "function" then
    local ok, value = pcall(C_AddOns.GetAddOnMetadata, addonName, field)
    if ok and value ~= nil then
      return tostring(value)
    end
  end
  if type(GetAddOnMetadata) == "function" then
    local ok, value = pcall(GetAddOnMetadata, addonName, field)
    if ok then
      return tostring(value or "")
    end
  end
  return ""
end

local function BuildProductionChecks()
  local checks = {
    {
      key = "api_registry",
      label = "Namespaced API registry",
      probe = function()
        return type(API) == "table", "resolved type " .. type(API)
      end,
    },
    {
      key = "internal_api_registry",
      label = "Private API registry",
      probe = function()
        return type(INTERNAL_API) == "table", "resolved type " .. type(INTERNAL_API)
      end,
    },
  }

  local criticalFactories = {
    { "LIDCOUNCIL_CreateProfileSyncStateStore_Impl", "Profile sync state-store factory" },
    { "LIDCOUNCIL_CreateProfileSyncDevHarness_Impl", "Profile sync validation factory" },
    { "LIDCOUNCIL_CreateLootSessionProfileSyncHelpers_Impl", "Profile synchronization factory" },
    { "LIDCOUNCIL_CreateLootSessionFlowHelpers_Impl", "Session flow factory" },
    { "LIDCOUNCIL_CreateLootSessionPayloadCodec_Impl", "Communication payload codec factory" },
    { "LIDCOUNCIL_CreateInboundAdmissionHelpers_Impl", "Inbound admission factory" },
    { "LIDCOUNCIL_CreateInboundRateLimitHelpers_Impl", "Inbound rate-limit factory" },
    { "LIDCOUNCIL_CreateOutboundTimerAdapterHelpers_Impl", "Outbound timer adapter factory" },
  }
  for _, definition in ipairs(criticalFactories) do
    checks[#checks + 1] = BuildFunctionCheck(INTERNAL_API, definition[1], definition[2])
  end

  local requiredApis = {
    { "LIDCOUNCIL_RootDB", "Root database API" },
    { "LIDCOUNCIL_GetCharacterKey", "Character identity API" },
    { "LIDCOUNCIL_GetActiveLootSession", "Active session API" },
    { "LIDCOUNCIL_GetLootProfile", "Loot profile API" },
    { "LIDCOUNCIL_StartLootSessionFlow", "Session start flow API" },
    { "LIDCOUNCIL_StopLootSessionFlow", "Session stop flow API" },
    { "LIDCOUNCIL_GetSessionCommQueueStatus", "Communication diagnostics API" },
    { "LIDCOUNCIL_GetCommProtocolHandlerStatus", "Protocol registry diagnostics API" },
    { "LIDCOUNCIL_RunReleasePreflightValidation", "Release preflight validation API" },
  }
  for _, definition in ipairs(requiredApis) do
    checks[#checks + 1] = BuildFunctionCheck(API, definition[1], definition[2])
  end

  checks[#checks + 1] = {
    key = "release_preflight_validation_binding",
    label = "Release preflight validation API binding",
    probe = function()
      local publicValidation = type(API) == "table" and API.LIDCOUNCIL_RunReleasePreflightValidation or nil
      local internalValidation = type(INTERNAL_API) == "table"
        and INTERNAL_API.LIDCOUNCIL_RunReleasePreflightValidation_Impl or nil
      if type(publicValidation) ~= "function" or type(internalValidation) ~= "function" then
        return false, "public type " .. type(publicValidation) .. ", private type " .. type(internalValidation)
      end
      return publicValidation == internalValidation,
        publicValidation == internalValidation and "bindings aligned" or "bindings resolve different functions"
    end,
  }

  checks[#checks + 1] = {
    key = "blizzard_addon_communication",
    label = "Blizzard addon communication APIs",
    probe = function()
      local ready = type(C_ChatInfo) == "table"
        and type(C_ChatInfo.RegisterAddonMessagePrefix) == "function"
        and type(C_ChatInfo.SendAddonMessage) == "function"
      return ready, ready and "available" or "C_ChatInfo communication functions unavailable"
    end,
  }
  checks[#checks + 1] = {
    key = "blizzard_runtime_apis",
    label = "Blizzard frame, build, and combat APIs",
    probe = function()
      local ready = type(CreateFrame) == "function"
        and type(GetBuildInfo) == "function"
        and type(InCombatLockdown) == "function"
      return ready, ready and "available" or "one or more runtime functions unavailable"
    end,
  }
  checks[#checks + 1] = {
    key = "timer_compatibility",
    label = "Timer compatibility layer",
    probe = function()
      local compat = rawget(_G, "LIDCOUNCIL_Compat")
      if type(compat) ~= "table" or type(compat.After) ~= "function" or type(compat.GetTimerStatus) ~= "function" then
        return false, "timer compatibility functions unavailable"
      end
      local statusOk, status = pcall(compat.GetTimerStatus)
      return statusOk and type(status) == "table", statusOk and "status available" or status
    end,
  }
  checks[#checks + 1] = {
    key = "profile_group_navigation",
    label = "Profile group navigation contract",
    probe = function()
      local shared = type(INTERNAL_API) == "table" and INTERNAL_API.LIDCOUNCIL_LootEditorShared_Impl or nil
      local validate = type(shared) == "table" and shared.RunLootProfileGroupNavigationValidation or nil
      if type(validate) ~= "function" then
        return false, "profile group navigation validator unavailable"
      end
      local ok, ready, report = pcall(validate)
      report = type(report) == "table" and report or {}
      local passed = math.max(0, tonumber(report.passed) or 0)
      local total = math.max(0, tonumber(report.total) or 0)
      return ok and ready == true and total > 0 and passed == total,
        ok and (tostring(passed) .. "/" .. tostring(total) .. " checks passed") or tostring(ready)
    end,
  }
  checks[#checks + 1] = {
    key = "raid_loot_variant_resolution",
    label = "Raid loot difficulty variant contract",
    probe = function()
      local shared = type(INTERNAL_API) == "table" and INTERNAL_API.LIDCOUNCIL_LootEditorShared_Impl or nil
      local validate = type(shared) == "table" and shared.RunRaidLootVariantResolutionValidation or nil
      if type(validate) ~= "function" then
        return false, "raid loot variant validator unavailable"
      end
      local ok, ready, report = pcall(validate)
      report = type(report) == "table" and report or {}
      local passed = math.max(0, tonumber(report.passed) or 0)
      local total = math.max(0, tonumber(report.total) or 0)
      return ok and ready == true and total > 0 and passed == total,
        ok and (tostring(passed) .. "/" .. tostring(total) .. " checks passed") or tostring(ready)
    end,
  }
  checks[#checks + 1] = {
    key = "loot_presets_hydration_lifecycle",
    label = "Loot Presets hydration lifecycle contract",
    probe = function()
      local validate = type(INTERNAL_API) == "table"
        and INTERNAL_API.LIDCOUNCIL_RunLootPresetsHydrationLifecycleValidation_Impl or nil
      if type(validate) ~= "function" then
        return false, "Loot Presets hydration validator unavailable"
      end
      local ok, ready, report = pcall(validate)
      report = type(report) == "table" and report or {}
      local passed = math.max(0, tonumber(report.passed) or 0)
      local total = math.max(0, tonumber(report.total) or 0)
      return ok and ready == true and total > 0 and passed == total,
        ok and (tostring(passed) .. "/" .. tostring(total) .. " checks passed") or tostring(ready)
    end,
  }
  checks[#checks + 1] = {
    key = "protocol_registry",
    label = "Communication protocol registry",
    probe = function()
      local getStatus = type(API) == "table" and API.LIDCOUNCIL_GetCommProtocolHandlerStatus or nil
      if type(getStatus) ~= "function" then
        return false, "status API unavailable"
      end
      local statusOk, status = pcall(getStatus)
      if not statusOk or type(status) ~= "table" then
        return false, statusOk and "invalid status result" or status
      end
      local detail = tostring(math.max(0, tonumber(status.handlerCount) or 0)) .. "/"
        .. tostring(math.max(0, tonumber(status.contractCount) or 0)) .. " handlers"
      return status.ready == true, detail
    end,
  }
  checks[#checks + 1] = {
    key = "interface_metadata",
    label = "TOC interface compatibility",
    probe = function()
      local metadata = ReadAddonMetadata("Interface")
      -- Parentheses retain only the interface return value; later GetBuildInfo values are strings.
      local currentInterface = type(GetBuildInfo) == "function" and tonumber((select(4, GetBuildInfo()))) or nil
      if metadata == "" then
        -- Modern clients may load the TOC but no longer expose its Interface field through metadata APIs.
        return true, currentInterface and ("client " .. tostring(currentInterface) .. "; loaded TOC metadata unavailable")
          or "loaded TOC metadata unavailable"
      end
      if not currentInterface then
        return true, "client interface value unavailable; loaded TOC metadata " .. metadata
      end
      for declared in string.gmatch(metadata, "%d+") do
        if tonumber(declared) == currentInterface then
          return true, metadata .. " includes " .. tostring(currentInterface)
        end
      end
      return false, metadata .. " does not include " .. tostring(currentInterface)
    end,
  }
  return checks
end

local function GetStartupHealthTime()
  if type(GetTime) == "function" then
    return math.max(0, tonumber(GetTime()) or 0)
  end
  return math.max(0, tonumber(time and time() or 0) or 0)
end

local startupHealth = INTERNAL_API.LIDCOUNCIL_CreateStartupHealthHelpers_Impl({
  BuildChecks = BuildProductionChecks,
  GetNow = GetStartupHealthTime,
  OnCompleted = function(report)
    if type(report) ~= "table" or math.max(0, tonumber(report.errorCount) or 0) == 0 then
      return
    end
    local logError = rawget(_G, "LIDCOUNCIL_LogError")
    if type(logError) == "function" then
      local firstProblem = type(report.problems) == "table" and tostring(report.problems[1] or "") or ""
      logError("[StartupHealth] " .. tostring(report.errorCount) .. " critical check(s) failed"
        .. (firstProblem ~= "" and (": " .. firstProblem) or "."))
    end
  end,
})

local function RunStartupHealthCheck(force)
  return startupHealth.Run(force == true)
end

local function GetStartupHealthStatus()
  return startupHealth.GetStatus()
end

local function RunStartupHealthValidation()
  local validationChecks = {}
  local validationHealth = INTERNAL_API.LIDCOUNCIL_CreateStartupHealthHelpers_Impl({
    GetNow = function()
      return 42
    end,
    BuildChecks = function()
      return {
        { key = "pass", label = "Passing check", probe = function() return true, "ok" end },
        { key = "warn", label = "Warning check", severity = "warning", probe = function() return false, "warning" end },
        { key = "error", label = "Error check", probe = function() error("fixture failure") end },
      }
    end,
  })
  local first = validationHealth.Run()
  local cached = validationHealth.Run()
  local productionChecks = BuildProductionChecks()
  local preflightValidationCheck
  local preflightBindingCheck
  for _, check in ipairs(productionChecks) do
    if check.key == "lidcouncil_runreleasepreflightvalidation" then
      preflightValidationCheck = check
    elseif check.key == "release_preflight_validation_binding" then
      preflightBindingCheck = check
    end
  end
  validationChecks[#validationChecks + 1] = first.ran == true
  validationChecks[#validationChecks + 1] = first.ready == false and first.severity == "error"
  validationChecks[#validationChecks + 1] = first.checkCount == 3 and first.passedCount == 1
  validationChecks[#validationChecks + 1] = first.warningCount == 1 and first.errorCount == 1
  validationChecks[#validationChecks + 1] = first.checkedAt == 42 and #first.problems == 2
  cached.problems[1] = "mutated"
  validationChecks[#validationChecks + 1] = validationHealth.GetStatus().problems[1] ~= "mutated"
  validationChecks[#validationChecks + 1] = #productionChecks == 28
  validationChecks[#validationChecks + 1] = type(preflightValidationCheck) == "table"
    and preflightValidationCheck.label == "Release preflight validation API"
  local preflightProbeOk, preflightReady = pcall(
    type(preflightValidationCheck) == "table" and preflightValidationCheck.probe or function() return false end
  )
  validationChecks[#validationChecks + 1] = preflightProbeOk and preflightReady == true
  validationChecks[#validationChecks + 1] = type(preflightBindingCheck) == "table"
    and preflightBindingCheck.label == "Release preflight validation API binding"
  local bindingProbeOk, bindingReady = pcall(
    type(preflightBindingCheck) == "table" and preflightBindingCheck.probe or function() return false end
  )
  validationChecks[#validationChecks + 1] = bindingProbeOk and bindingReady == true
  local passedCount = 0
  for _, passed in ipairs(validationChecks) do
    if passed then
      passedCount = passedCount + 1
    end
  end
  return passedCount == #validationChecks, {
    checks = #validationChecks,
    passed = passedCount,
    failed = #validationChecks - passedCount,
  }
end

API.LIDCOUNCIL_GetStartupHealthStatus = GetStartupHealthStatus
API.LIDCOUNCIL_RunStartupHealthCheck = RunStartupHealthCheck
API.LIDCOUNCIL_RunStartupHealthValidation = RunStartupHealthValidation
INTERNAL_API.LIDCOUNCIL_GetStartupHealthStatus_Impl = GetStartupHealthStatus
INTERNAL_API.LIDCOUNCIL_RunStartupHealthCheck_Impl = RunStartupHealthCheck

local function ScheduleStartupHealthCheck()
  local compat = rawget(_G, "LIDCOUNCIL_Compat")
  if type(compat) == "table" and type(compat.After) == "function" then
    local scheduledOk, scheduled = pcall(compat.After, 0, RunStartupHealthCheck)
    if scheduledOk and scheduled ~= false then
      return
    end
  end
  RunStartupHealthCheck()
end

if type(CreateFrame) == "function" then
  local eventFrame = CreateFrame("Frame")
  eventFrame:RegisterEvent("PLAYER_LOGIN")
  eventFrame:SetScript("OnEvent", function(self)
    self:UnregisterEvent("PLAYER_LOGIN")
    ScheduleStartupHealthCheck()
  end)
else
  RunStartupHealthCheck()
end
