import type { ApiAlertGroup, ApiAlertRule } from "../alerts-service.js"
import { computeStabilityReadyMap } from "./confirm-stability.js"
import { isCooldownElapsed } from "./cooldown.js"
import { groupShouldFire } from "./groups.js"
import { isPositiveRecoveryTelegramText } from "./telegram-emoji.js"
import { shouldAllowRecoveryByPolicy } from "./state-machine.js"
import type { AlertEngineRuleDiag, RuleEvalHit } from "./types.js"

export type EngineStateMap = Map<string, { lastFiredAt: string; lastPayloadHash: string | null }>

export type StandaloneDecision = {
  kind: "rule"
  rule: ApiAlertRule
  hit: RuleEvalHit
}

export type GroupDecision = {
  kind: "group"
  group: ApiAlertGroup
  members: ApiAlertRule[]
  hits: RuleEvalHit[]
}

function ruleScopeKey(ruleId: string, transition?: RuleEvalHit["transition"]): string {
  const suffix = transition ?? "neutral"
  return `rule:${ruleId}:${suffix}`
}

function textAfterGroupAlertHeader(fullBody: string): string {
  const segs = fullBody.split(/\n\n/)
  if (segs.length >= 2 && /^Группа\s*«/i.test(segs[0] ?? "")) return segs.slice(1).join("\n\n").trim()
  return fullBody
}

export function buildRuleDiagnostics(
  rules: ApiAlertRule[],
  hitByRule: Map<string, RuleEvalHit | null>,
  stabilityReady: Map<string, boolean>,
  state: EngineStateMap,
  canSend: boolean,
): AlertEngineRuleDiag[] {
  const out: AlertEngineRuleDiag[] = []
  for (const rule of rules) {
    if (!rule.enabled) continue
    const hit = hitByRule.get(rule.id) ?? null
    const inGroup = Boolean(rule.groupId)
    const evalHit = Boolean(hit)
    const stab = stabilityReady.get(rule.id) ?? false
    const key = ruleScopeKey(rule.id, hit?.transition)
    const prev = state.get(key)
    const cooldownOk = isCooldownElapsed(prev?.lastFiredAt, rule.cooldown)
    let blocked: AlertEngineRuleDiag["blocked"]
    if (!evalHit) blocked = "no_hit"
    else if (!shouldAllowRecoveryByPolicy(hit, rule.recoveryMode)) blocked = "stability"
    else if (inGroup) blocked = "in_group"
    else if (!stab) blocked = "stability"
    else if (!cooldownOk) blocked = "cooldown"
    else if (!canSend) blocked = "no_telegram"
    else if (hit && isPositiveRecoveryTelegramText(hit.message) && prev?.lastPayloadHash === hit.payloadHash) blocked = "dedupe_positive"
    else blocked = undefined
    out.push({
      ruleId: rule.id,
      inGroup,
      evalHit,
      hitTransition: hit?.transition,
      hitMessage: hit?.message,
      stabilityOk: stab,
      cooldownOk,
      telegramOk: canSend,
      blocked,
    })
  }
  return out
}

export function computeDecisions(args: {
  rules: ApiAlertRule[]
  groups: ApiAlertGroup[]
  hitByRule: Map<string, RuleEvalHit | null>
  state: EngineStateMap
  canSend: boolean
}): {
  stabilityReady: Map<string, boolean>
  standalone: StandaloneDecision[]
  grouped: GroupDecision[]
  ruleDiag: AlertEngineRuleDiag[]
} {
  const { rules, groups, hitByRule, state, canSend } = args
  const stabilityReady = computeStabilityReadyMap(rules, hitByRule)
  const ruleDiag = buildRuleDiagnostics(rules, hitByRule, stabilityReady, state, canSend)
  const standalone: StandaloneDecision[] = []
  const grouped: GroupDecision[] = []

  for (const rule of rules) {
    if (!rule.enabled || rule.groupId) continue
    const hit = hitByRule.get(rule.id)
    if (!hit) continue
    if (!shouldAllowRecoveryByPolicy(hit, rule.recoveryMode)) continue
    if (!stabilityReady.get(rule.id)) continue
    const key = ruleScopeKey(rule.id, hit.transition)
    const prev = state.get(key)
    if (!isCooldownElapsed(prev?.lastFiredAt, rule.cooldown)) continue
    if (isPositiveRecoveryTelegramText(hit.message) && prev?.lastPayloadHash === hit.payloadHash) continue
    standalone.push({ kind: "rule", rule, hit })
  }

  for (const g of groups) {
    if (!g.enabled) continue
    const members = rules.filter((r) => r.groupId === g.id && r.enabled)
    if (members.length === 0) continue
    const firing = members.map((r) => {
      const h = hitByRule.get(r.id)
      if (!shouldAllowRecoveryByPolicy(h ?? null, r.recoveryMode)) return false
      return Boolean(h) && Boolean(stabilityReady.get(r.id))
    })
    if (!groupShouldFire(g.combineMode, firing)) continue
    const hits: RuleEvalHit[] = []
    for (const member of members) {
      const hit = hitByRule.get(member.id)
      if (!hit) continue
      if (!shouldAllowRecoveryByPolicy(hit, member.recoveryMode)) continue
      hits.push(hit)
    }
    if (hits.length === 0) continue
    const key = `group:${g.id}`
    const prev = state.get(key)
    const cd = g.cooldownOverride ?? "5м"
    if (!isCooldownElapsed(prev?.lastFiredAt, cd)) continue
    const body = `Группа «${g.name}» (${g.combineMode === "any" ? "ANY" : "ALL"})\n\n${hits.map((h) => h.message).join("\n")}`
    const hash = hits.map((h) => h.payloadHash).sort().join("|")
    if (isPositiveRecoveryTelegramText(textAfterGroupAlertHeader(body)) && prev?.lastPayloadHash === hash) continue
    grouped.push({ kind: "group", group: g, members, hits })
  }

  return { stabilityReady, standalone, grouped, ruleDiag }
}
