-- Isolated contract for explicit, host-authoritative profile recovery pushes.

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 Contract = {}
local CONTRACT_VERSION = 1

local SCOPE_ORDER = {
  "full_profile",
  "profile_setup",
  "current_session",
  "roles_rights",
  "loot_list",
  "item_history",
  "session_history",
  "fun_awards",
}

local SCOPE_ALIASES = {
  full = "full_profile",
  profile = "full_profile",
  settings = "profile_setup",
  current = "current_session",
  p2 = "current_session",
  permissions = "roles_rights",
  rights = "roles_rights",
  loot = "loot_list",
  items = "item_history",
  sessions = "session_history",
  awards = "fun_awards",
}

local SCOPE_DEFS = {
  full_profile = {
    key = "full_profile",
    label = "Complete Shared Profile",
    description = "Replace every shared profile section on one joined member.",
    transport = {
      mode = "BACKGROUND",
      reason = "host_override_full_profile",
      forceFullSnapshot = true,
    },
  },
  profile_setup = {
    key = "profile_setup",
    label = "Profile Setup",
    description = "Replace raid setup, profile image, roll options, and point configuration.",
    transport = {
      mode = "LIVE",
      reason = "host_override_profile_setup",
      domains = { "settings" },
    },
  },
  current_session = {
    key = "current_session",
    label = "Current Session State",
    description = "Replace only the canonical current-session record used by P2 readiness.",
    transport = {
      mode = "LIVE",
      reason = "host_override_current_session",
      domains = { "session" },
      currentSessionOnly = true,
    },
  },
  roles_rights = {
    key = "roles_rights",
    label = "Roles & Rights",
    description = "Replace profile members, roles, and the role-rights policy.",
    transport = {
      mode = "LIVE",
      reason = "host_override_roles_rights",
      domains = { "permissions" },
    },
  },
  loot_list = {
    key = "loot_list",
    label = "Loot List and Character Details",
    description = "Authoritatively replace Loot List characters, links, notes, awards, and adjustments.",
    transport = {
      mode = "BACKGROUND",
      reason = "host_override_loot_list",
      domains = { "loot" },
      authoritativeArchiveScope = "LOOT_LIST",
    },
  },
  item_history = {
    key = "item_history",
    label = "Item History",
    description = "Replace Live Board item, assignment, and trade history.",
    transport = {
      mode = "BACKGROUND",
      reason = "host_override_item_history",
      domains = { "session" },
      subdomains = { "session_live_board" },
    },
  },
  session_history = {
    key = "session_history",
    label = "Session History",
    description = "Authoritatively replace completed and retained session records.",
    transport = {
      mode = "BACKGROUND",
      reason = "host_override_session_history",
      domains = { "session" },
      authoritativeArchiveScope = "SESSION_HISTORY",
    },
  },
  fun_awards = {
    key = "fun_awards",
    label = "Fun Award Events",
    description = "Authoritatively replace canonical real roll and council-vote events.",
    dependencies = { "loot_list" },
    transport = {
      mode = "BACKGROUND",
      reason = "host_override_fun_awards",
      domains = { "loot" },
      authoritativeArchiveScope = "FUN_AWARDS",
    },
  },
}

local function Trim(value)
  local text = tostring(value or "")
  text = text:gsub("^%s+", "")
  text = text:gsub("%s+$", "")
  return text
end

local function Copy(value, seen)
  if type(value) ~= "table" then
    return value
  end
  seen = type(seen) == "table" and seen or {}
  if seen[value] then
    return seen[value]
  end
  local result = {}
  seen[value] = result
  for key, entry in pairs(value) do
    result[Copy(key, seen)] = Copy(entry, seen)
  end
  return result
end

local function SplitRequestedScopes(requested)
  if type(requested) == "table" then
    return requested
  end
  local scopes = {}
  for token in tostring(requested or ""):gmatch("[^,]+") do
    scopes[#scopes + 1] = token
  end
  return scopes
end

function Contract.GetScopeRegistry()
  local registry = {}
  for _, key in ipairs(SCOPE_ORDER) do
    registry[#registry + 1] = Copy(SCOPE_DEFS[key])
  end
  return registry
end

function Contract.NormalizeScopes(requested)
  local selected = {}
  local unknown = {}
  for _, value in ipairs(SplitRequestedScopes(requested)) do
    local key = string.lower(Trim(value)):gsub("[%s%-]+", "_")
    key = SCOPE_ALIASES[key] or key
    if SCOPE_DEFS[key] then
      selected[key] = true
    elseif key ~= "" then
      unknown[#unknown + 1] = key
    end
  end
  if #unknown > 0 then
    return nil, "Unknown host override scope: " .. table.concat(unknown, ", ")
  end
  if selected.full_profile then
    return { "full_profile" }
  end
  local normalized = {}
  for _, key in ipairs(SCOPE_ORDER) do
    if selected[key] then
      normalized[#normalized + 1] = key
    end
  end
  if #normalized == 0 then
    return nil, "Select at least one host override scope."
  end
  return normalized
end

function Contract.BuildPlan(requested)
  local scopes, scopeError = Contract.NormalizeScopes(requested)
  if not scopes then
    return nil, scopeError
  end
  local included = {}
  for _, scope in ipairs(scopes) do
    included[scope] = true
  end
  local steps = {}
  local warnings = {}
  for _, scope in ipairs(scopes) do
    local definition = SCOPE_DEFS[scope]
    local transport = Copy(definition.transport)
    transport.forceAuthoritativeOverride = true
    transport.skipScopePrune = true
    transport.suppressUnchanged = false
    steps[#steps + 1] = {
      scope = scope,
      label = definition.label,
      transport = transport,
    }
    for _, dependency in ipairs(type(definition.dependencies) == "table" and definition.dependencies or {}) do
      if included[dependency] ~= true then
        warnings[#warnings + 1] = tostring(definition.label) .. " is most reliable when "
          .. tostring(SCOPE_DEFS[dependency] and SCOPE_DEFS[dependency].label or dependency) .. " is included."
      end
    end
  end
  return {
    version = CONTRACT_VERSION,
    scopes = Copy(scopes),
    steps = steps,
    warnings = warnings,
    targetPolicy = "single_joined_member",
    senderPolicy = "active_session_host",
    receiverPolicy = "automatic_active_host",
    comparisonPolicy = "bypass",
    requiresSenderConfirmation = true,
    requiresRecipientConfirmation = false,
  }
end

function Contract.ExecutePlan(targetMemberKey, requested, dispatch)
  local target = Trim(targetMemberKey)
  if target == "" then
    return false, { error = "Select one joined member for the host override." }
  end
  if type(dispatch) ~= "function" then
    return false, { error = "Host override transport is unavailable." }
  end
  local plan, planError = Contract.BuildPlan(requested)
  if not plan then
    return false, { error = planError }
  end
  local report = {
    target = target,
    scopes = Copy(plan.scopes),
    warnings = Copy(plan.warnings),
    attemptedSteps = 0,
    acceptedSteps = 0,
    queuedTransfers = 0,
    failures = {},
    results = {},
  }
  for _, step in ipairs(plan.steps) do
    report.attemptedSteps = report.attemptedSteps + 1
    local executed, accepted, transferCount, errorText = pcall(
      dispatch,
      target,
      Copy(step.transport),
      Copy(step)
    )
    local stepAccepted = executed and accepted == true
    if stepAccepted then
      report.acceptedSteps = report.acceptedSteps + 1
      report.queuedTransfers = report.queuedTransfers + math.max(0, tonumber(transferCount) or 0)
    else
      local resolvedError = executed and errorText or accepted
      if executed and Trim(resolvedError) == "" and type(transferCount) == "string" then
        resolvedError = transferCount
      end
      report.failures[#report.failures + 1] = {
        scope = step.scope,
        error = Trim(resolvedError) ~= "" and Trim(resolvedError) or "Host override dispatch failed.",
      }
    end
    report.results[#report.results + 1] = {
      scope = step.scope,
      accepted = stepAccepted,
      transfers = stepAccepted and math.max(0, tonumber(transferCount) or 0) or 0,
    }
  end
  report.complete = report.acceptedSteps == report.attemptedSteps and #report.failures == 0
  return report.complete, report
end

function Contract.RunValidation()
  local checks = {}
  local problems = {}
  local function Check(label, passed)
    checks[#checks + 1] = { label = label, passed = passed == true }
    if passed ~= true then
      problems[#problems + 1] = label
    end
  end

  local registry = Contract.GetScopeRegistry()
  Check("override registry exposes eight deterministic scopes", #registry == 8
    and registry[1].key == "full_profile" and registry[8].key == "fun_awards")
  local aliases = Contract.NormalizeScopes({ "settings", "permissions", "loot" })
  Check("override aliases normalize in canonical order", aliases and aliases[1] == "profile_setup"
    and aliases[2] == "roles_rights" and aliases[3] == "loot_list")
  local full = Contract.NormalizeScopes({ "loot_list", "full_profile" })
  Check("full override supersedes narrower scopes", full and #full == 1 and full[1] == "full_profile")
  Check("unknown override scope is rejected", Contract.NormalizeScopes({ "unknown_scope" }) == nil)
  Check("empty override scope is rejected", Contract.NormalizeScopes({}) == nil)

  local plan = Contract.BuildPlan({
    "profile_setup",
    "current_session",
    "roles_rights",
    "loot_list",
    "item_history",
    "session_history",
    "fun_awards",
  })
  Check("override plan requires one joined member and active host", plan
    and plan.targetPolicy == "single_joined_member" and plan.senderPolicy == "active_session_host")
  Check("override plan requires host confirmation but not recipient confirmation", plan
    and plan.requiresSenderConfirmation == true and plan.requiresRecipientConfirmation == false)
  Check("every override step bypasses comparison pruning", plan and #plan.steps == 7
    and plan.steps[1].transport.forceAuthoritativeOverride == true
    and plan.steps[7].transport.skipScopePrune == true
    and plan.steps[7].transport.suppressUnchanged == false)
  Check("current session override resolves the active P2 leaf at dispatch time", plan
    and plan.steps[2].transport.domains[1] == "session"
    and plan.steps[2].transport.currentSessionOnly == true
    and plan.steps[2].transport.mode == "LIVE")
  Check("profile setup and rights remain live-priority domain transfers", plan
    and plan.steps[1].transport.domains[1] == "settings" and plan.steps[1].transport.mode == "LIVE"
    and plan.steps[3].transport.domains[1] == "permissions" and plan.steps[3].transport.mode == "LIVE")
  Check("Loot List override is authoritative archive replacement", plan
    and plan.steps[4].transport.authoritativeArchiveScope == "LOOT_LIST")
  Check("item history override targets only Live Board history", plan
    and plan.steps[5].transport.subdomains[1] == "session_live_board")
  Check("session history override is authoritative archive replacement", plan
    and plan.steps[6].transport.authoritativeArchiveScope == "SESSION_HISTORY")
  Check("Fun Awards override is authoritative archive replacement", plan
    and plan.steps[7].transport.authoritativeArchiveScope == "FUN_AWARDS")
  local awardsOnly = Contract.BuildPlan({ "fun_awards" })
  Check("Fun Awards override reports its Loot List dependency", awardsOnly and #awardsOnly.warnings == 1)
  local fullPlan = Contract.BuildPlan({ "full_profile" })
  Check("full override requests one forced background snapshot", fullPlan and #fullPlan.steps == 1
    and fullPlan.steps[1].transport.forceFullSnapshot == true
    and fullPlan.steps[1].transport.mode == "BACKGROUND")
  local emptyTargetOk = Contract.ExecutePlan("", { "profile_setup" }, function() return true, 1 end)
  Check("override executor rejects an empty member target", emptyTargetOk == false)
  local captured = {}
  local executedOk, execution = Contract.ExecutePlan("Member-Realm", { "roles_rights", "loot_list" }, function(target, transport, step)
    captured[#captured + 1] = { target = target, transport = transport, scope = step.scope }
    return true, step.scope == "loot_list" and 2 or 1
  end)
  Check("override executor dispatches canonical steps and aggregates transfers", executedOk
    and execution.acceptedSteps == 2 and execution.queuedTransfers == 3
    and captured[1].scope == "roles_rights" and captured[2].scope == "loot_list")
  Check("override executor preserves force and authoritative transport flags", captured[1]
    and captured[1].transport.forceAuthoritativeOverride == true
    and captured[1].transport.suppressUnchanged == false
    and captured[2].transport.authoritativeArchiveScope == "LOOT_LIST")
  local partialOk, partial = Contract.ExecutePlan("Member-Realm", { "profile_setup", "roles_rights" }, function(_, _, step)
    if step.scope == "roles_rights" then
      return false, 0, "fixture failure"
    end
    return true, 1
  end)
  Check("override executor reports partial dispatch failure without hiding accepted steps", partialOk == false
    and partial and partial.acceptedSteps == 1 and #partial.failures == 1
    and partial.failures[1].scope == "roles_rights")
  Check("host override transport API is published", type(API.LIDCOUNCIL_PushProfileHostOverrideFlow) == "function")
  Check("host override Sync Management view helpers are published",
    type(INTERNAL_API.LIDCOUNCIL_RefreshLootSessionHostOverridePanel_Impl) == "function"
      and type(INTERNAL_API.LIDCOUNCIL_LayoutLootSessionHostOverridePanel_Impl) == "function")
  local normalizeViewSelection = INTERNAL_API.LIDCOUNCIL_NormalizeProfileHostOverrideViewSelection_Impl
  local normalizedViewSelection = type(normalizeViewSelection) == "function"
    and normalizeViewSelection({ full_profile = true, loot_list = true })
    or nil
  Check("host override view makes complete profile supersede narrower selections",
    normalizedViewSelection and #normalizedViewSelection == 1 and normalizedViewSelection[1] == "full_profile")
  local collectQueuedScopes = INTERNAL_API.LIDCOUNCIL_CollectQueuedProfileHostOverrideScopes_Impl
  local queuedViewScopes = type(collectQueuedScopes) == "function" and collectQueuedScopes({
    results = {
      { scope = "current_session", accepted = true, transfers = 1 },
      { scope = "session_history", accepted = true, transfers = 0 },
      { scope = "loot_list", accepted = false, transfers = 1 },
    },
  }) or nil
  Check("host override view tracks only scopes with queued transports", queuedViewScopes
    and #queuedViewScopes == 1 and queuedViewScopes[1] == "current_session")
  local buildDeliveryStatus = INTERNAL_API.LIDCOUNCIL_BuildProfileHostOverrideDeliveryStatus_Impl
  local scopeLookup = {}
  for _, definition in ipairs(registry) do
    scopeLookup[definition.key] = definition
  end
  local deliveryStatus = type(buildDeliveryStatus) == "function" and buildDeliveryStatus({
    profileTransfers = {
      {
        transferId = "fixture-settings",
        target = "Member-Realm",
        sessionId = "SESSION-FIXTURE",
        reason = "host_override_profile_setup",
        state = "sending",
        sentChunks = 5,
        totalChunks = 10,
        queuedAt = 101,
        generation = 1,
      },
      {
        transferId = "fixture-awards",
        target = "Member-Realm",
        sessionId = "SESSION-FIXTURE",
        reason = "host_override_fun_awards",
        state = "waiting_ack",
        sentChunks = 3,
        totalChunks = 3,
        queuedAt = 101,
        generation = 1,
      },
    },
  }, {
    target = "Member-Realm",
    sessionId = "SESSION-FIXTURE",
    scopes = { "profile_setup", "fun_awards" },
    startedAt = 100,
  }, scopeLookup) or nil
  Check("host override view reports sending progress and receipt wait per scope", deliveryStatus
    and #deliveryStatus.rows == 2
    and deliveryStatus.rows[1].phase == "sending"
    and deliveryStatus.rows[1].detail == "Sending 50% (5/10 chunks)"
    and deliveryStatus.rows[2].phase == "waiting_ack"
    and deliveryStatus.overallPhase == "waiting_ack")
  local recoveredDelivery = type(buildDeliveryStatus) == "function" and buildDeliveryStatus({
    profileTransfers = {
      {
        transferId = "fixture-recovered-awards",
        target = "Member-Realm",
        sessionId = "SESSION-FIXTURE",
        reason = "outbound_ack_timeout_reconcile_fun_awards",
        hostAuthoritativeOverride = true,
        authoritativeArchiveScope = "FUN_AWARDS",
        state = "acked",
        queuedAt = 102,
        generation = 2,
      },
    },
  }, {
    target = "Member-Realm",
    sessionId = "SESSION-FIXTURE",
    scopes = { "fun_awards" },
    startedAt = 100,
  }, scopeLookup) or nil
  Check("host override delivery follows timeout recovery transfers to confirmed receipt", recoveredDelivery
    and recoveredDelivery.complete == true
    and recoveredDelivery.rows[1].phase == "complete")
  local exhaustedDelivery = type(buildDeliveryStatus) == "function" and buildDeliveryStatus({
    profileTransfers = {
      {
        transferId = "fixture-exhausted-current-session",
        target = "Member-Realm",
        sessionId = "SESSION-FIXTURE",
        reason = "host_override_current_session",
        state = "ack_timeout",
        ackTimeoutRecoveryOutcome = "retry_exhausted",
        ackTimeoutRecoveryError = "bounded retry exhausted",
        queuedAt = 102,
        generation = 1,
      },
    },
  }, {
    target = "Member-Realm",
    sessionId = "SESSION-FIXTURE",
    scopes = { "current_session" },
    startedAt = 100,
  }, scopeLookup) or nil
  Check("host override delivery makes exhausted receipt recovery terminal", exhaustedDelivery
    and exhaustedDelivery.complete == false
    and exhaustedDelivery.overallPhase == "failed"
    and exhaustedDelivery.rows[1].phase == "failed")
  local isDeliveryActive = INTERNAL_API.LIDCOUNCIL_IsProfileHostOverrideDeliveryActive_Impl
  Check("host override view locks duplicate dispatch only while delivery is active",
    type(isDeliveryActive) == "function"
      and isDeliveryActive(deliveryStatus) == true
      and isDeliveryActive(recoveredDelivery) == false
      and isDeliveryActive(exhaustedDelivery) == false
      and isDeliveryActive({ rows = { { phase = "failed" } } }) == false)
  local isOperationActive = INTERNAL_API.LIDCOUNCIL_IsProfileHostOverrideOperationActive_Impl
  Check("host override view locks accepted dispatch while queue telemetry catches up",
    type(isOperationActive) == "function"
      and isOperationActive({ target = "Member-Realm" }, nil) == true
      and isOperationActive({ target = "Member-Realm" }, deliveryStatus) == true
      and isOperationActive({ target = "Member-Realm" }, exhaustedDelivery) == false
      and isOperationActive(nil, deliveryStatus) == false)
  local isActiveHostOverride = INTERNAL_API.LIDCOUNCIL_IsActiveHostAuthoritativeProfileOverride_Impl
  Check("receiver honors override policy only for the active session host", type(isActiveHostOverride) == "function"
    and isActiveHostOverride({ hostAuthoritativeOverride = true }, { host = "Host-Realm" }, "Host-Realm") == true
    and isActiveHostOverride({ hostAuthoritativeOverride = true }, { host = "Host-Realm" }, "Member-Realm") == false
    and isActiveHostOverride({}, { host = "Host-Realm" }, "Host-Realm") == false)

  local passed = 0
  for _, entry in ipairs(checks) do
    if entry.passed then
      passed = passed + 1
    end
  end
  return passed == #checks, {
    version = CONTRACT_VERSION,
    total = #checks,
    passed = passed,
    failed = #checks - passed,
    checks = checks,
    problems = problems,
  }
end

INTERNAL_API.LIDCOUNCIL_ProfileHostOverrideContract = Contract
API.LIDCOUNCIL_GetProfileHostOverrideScopeRegistry = Contract.GetScopeRegistry
API.LIDCOUNCIL_NormalizeProfileHostOverrideScopes = Contract.NormalizeScopes
API.LIDCOUNCIL_BuildProfileHostOverridePlan = Contract.BuildPlan
API.LIDCOUNCIL_ExecuteProfileHostOverridePlan = Contract.ExecutePlan
API.LIDCOUNCIL_RunProfileHostOverrideContractValidation = Contract.RunValidation
