export const ACTION_META = {
  "work-next": {
    tone: "good",
    label: "work-next",
    description: "Best next executable task.",
  },
  "close-out": {
    tone: "good",
    label: "close-out",
    description: "Finish closure review and mark done when appropriate.",
  },
  revisit: {
    tone: "warn",
    label: "revisit",
    description: "Re-read and refresh before execution.",
  },
  split: {
    tone: "warn",
    label: "split",
    description: "Break an oversized workplan into smaller plans.",
  },
  park: {
    tone: "muted",
    label: "park",
    description: "Move out of active focus.",
  },
  "needs-human": {
    tone: "bad",
    label: "needs-human",
    description: "Human decision or approval needed.",
  },
  "needs-cross-agent": {
    tone: "bad",
    label: "needs-cross-agent",
    description: "Another repo or agent is the right owner.",
  },
  "needs-consistency-sync": {
    tone: "bad",
    label: "needs-consistency-sync",
    description: "File, DB, or index state should be reconciled.",
  },
};

const WORKPLAN_ID_RE = /([a-z0-9]+-wp-\d+[a-z]?)/i;
const ADHOC_ID_RE = /(adhoc-\d{4}-\d{2}-\d{2})/i;

export function normalizeCandidate(value) {
  return String(value ?? "")
    .trim()
    .replace(/\.md$/i, "")
    .replace(/^workplans\//i, "")
    .replace(/^archived\//i, "")
    .replace(/_/g, "-")
    .replace(/\s+/g, "-")
    .toLowerCase();
}

function filenameStem(filename) {
  const base = String(filename ?? "").split("/").pop() ?? "";
  return base.replace(/\.md$/i, "").replace(/^\d{6}-/, "");
}

export function candidateKeysForWorkplan(item = {}) {
  const stem = filenameStem(item.filename ?? item.relative_path);
  const keys = new Set();
  const normalizedStem = normalizeCandidate(stem);
  if (normalizedStem) keys.add(normalizedStem);

  const idMatch = stem.match(WORKPLAN_ID_RE) ?? stem.match(ADHOC_ID_RE);
  if (idMatch) {
    const idKey = normalizeCandidate(idMatch[1]);
    keys.add(idKey);

    const suffix = normalizeCandidate(stem.slice(idMatch.index + idMatch[1].length).replace(/^-/, ""));
    if (suffix) keys.add(suffix);
  }

  return [...keys].filter(Boolean);
}

export function buildCandidateIndex(workplanIndex = {}) {
  const byCandidate = new Map();
  const workstreams = workplanIndex.workstreams ?? {};
  for (const [id, item] of Object.entries(workstreams)) {
    const resolved = {id, ...item};
    byCandidate.set(normalizeCandidate(id), resolved);
    for (const key of candidateKeysForWorkplan(item)) {
      if (!byCandidate.has(key)) byCandidate.set(key, resolved);
    }
  }
  return byCandidate;
}

export function resolveCandidate(candidate, candidateIndex) {
  if (!candidateIndex) return null;
  const key = normalizeCandidate(candidate);
  return candidateIndex.get(key) ?? null;
}

export function actionMeta(action) {
  return ACTION_META[normalizeCandidate(action)] ?? {
    tone: "muted",
    label: String(action ?? "unknown"),
    description: "Unrecognized recommendation action.",
  };
}

export function normalizeTriageReports(events = []) {
  return events
    .map((event, index) => {
      const detail = event?.detail ?? {};
      const report = detail.report ?? {};
      const recommendations = Array.isArray(report.recommendations)
        ? report.recommendations.map((rec, recIndex) => ({
            rank: recIndex + 1,
            candidate: String(rec?.candidate ?? "").trim(),
            action: normalizeCandidate(rec?.action) || "unknown",
            confidence: String(rec?.confidence ?? "").trim() || "unknown",
            why: String(rec?.why ?? "").trim(),
          }))
        : [];
      const scheduledFor = detail.scheduled_for ?? null;
      const runId = detail.activity_core_run_id ?? null;
      const dateSource = scheduledFor ?? event?.created_at ?? "";
      const date = String(dateSource).slice(0, 10) || null;

      return {
        id: String(event?.id ?? `${event?.created_at ?? "report"}-${index}`),
        created_at: event?.created_at ?? null,
        date,
        summary: String(report.summary ?? event?.summary ?? "").trim(),
        recommendations,
        scheduled_for: scheduledFor,
        activity_core_run_id: runId,
        instruction_id: detail.instruction_id ?? null,
        activity_id: detail.activity_id ?? null,
        author: event?.author ?? null,
        memory_path: detail.memory_path ?? detail.working_memory_path ?? deriveMemoryPath(date, runId),
      };
    })
    .sort((a, b) => String(b.created_at ?? "").localeCompare(String(a.created_at ?? "")));
}

export function deriveMemoryPath(date, runId) {
  if (!date || !runId) return null;
  return `the-custodian/memory/working/daily-triage-${date}-${String(runId).slice(0, 8)}.md`;
}

export function truncateSummary(summary, maxLength = 120) {
  const text = String(summary ?? "").trim();
  if (text.length <= maxLength) return text;
  return `${text.slice(0, Math.max(0, maxLength - 3)).trimEnd()}...`;
}

export function topAction(recommendations = []) {
  const counts = new Map();
  for (const rec of recommendations) {
    const action = normalizeCandidate(rec?.action) || "unknown";
    counts.set(action, (counts.get(action) ?? 0) + 1);
  }
  const [action, count] = [...counts.entries()].sort((a, b) => {
    const countCompare = b[1] - a[1];
    return countCompare || a[0].localeCompare(b[0]);
  })[0] ?? [];
  return action ? {action, count} : null;
}

export function formatActionCount(row) {
  return row ? `${row.action} x${row.count}` : "-";
}

export function isWithinDays(iso, days, now = new Date()) {
  if (!iso) return false;
  const ts = new Date(iso).getTime();
  if (Number.isNaN(ts)) return false;
  return now.getTime() - ts <= days * 24 * 60 * 60 * 1000;
}

export function buildPatternRows(reports = []) {
  const rows = new Map();
  for (const report of reports) {
    for (const rec of report.recommendations ?? []) {
      const key = normalizeCandidate(rec.candidate);
      if (!key) continue;
      const row = rows.get(key) ?? {
        candidate: rec.candidate,
        candidateKey: key,
        count: 0,
        actionCounts: new Map(),
      };
      row.count += 1;
      const action = normalizeCandidate(rec.action) || "unknown";
      row.actionCounts.set(action, (row.actionCounts.get(action) ?? 0) + 1);
      rows.set(key, row);
    }
  }

  return [...rows.values()]
    .map(row => {
      const top = [...row.actionCounts.entries()].sort((a, b) => {
        const countCompare = b[1] - a[1];
        return countCompare || a[0].localeCompare(b[0]);
      })[0];
      return {
        candidate: row.candidate,
        candidateKey: row.candidateKey,
        count: row.count,
        action: top?.[0] ?? "unknown",
        actionCount: top?.[1] ?? 0,
      };
    })
    .sort((a, b) => {
      const countCompare = b.count - a.count;
      return countCompare || a.candidateKey.localeCompare(b.candidateKey);
    });
}
