"use client";

import { useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Switch } from "@/components/ui/switch";

// ---------------------------------------------------------------------------
// Wire types (matches /api/council/consult response)
// ---------------------------------------------------------------------------

export type CouncilModelId = "anthropic" | "openai" | "gemini";

export interface CouncilVerdict {
  model: CouncilModelId;
  ok: boolean;
  message?: string;
  error?: string;
  latencyMs?: number;
}

// ---------------------------------------------------------------------------
// Parsed stance derived from free-text message
// ---------------------------------------------------------------------------

export type CouncilStance = "agree" | "disagree" | "unclear";
export type CouncilConfidence = "high" | "medium" | "low";

export interface ParsedVerdict {
  model: CouncilModelId;
  stance: CouncilStance;
  confidence: CouncilConfidence;
  reasoning: string;
}

const MODEL_DISPLAY: Record<CouncilModelId, { label: string; icon: string }> = {
  anthropic: { label: "Anthropic Claude", icon: "🟧" },
  openai: { label: "OpenAI GPT-4o", icon: "🟢" },
  gemini: { label: "Google Gemini", icon: "🔵" },
};

// ---------------------------------------------------------------------------
// Parsing — we prompt each model to start its reply with two header lines:
//   STANCE: AGREE | DISAGREE | UNCLEAR
//   CONFIDENCE: HIGH | MEDIUM | LOW
// Everything after those two lines is treated as the reasoning paragraph.
// If the headers are missing we default to "unclear" / "low".
// ---------------------------------------------------------------------------

export function parseVerdict(v: CouncilVerdict): ParsedVerdict {
  if (!v.ok || !v.message) {
    return { model: v.model, stance: "unclear", confidence: "low", reasoning: v.error ?? "No response." };
  }
  const text = v.message.trim();
  const stanceMatch = text.match(/^\s*STANCE\s*:\s*(AGREE|DISAGREE|UNCLEAR)/im);
  const confMatch = text.match(/CONFIDENCE\s*:\s*(HIGH|MEDIUM|LOW)/im);

  let stance: CouncilStance = "unclear";
  if (stanceMatch) {
    const raw = stanceMatch[1].toLowerCase();
    if (raw === "agree" || raw === "disagree" || raw === "unclear") stance = raw;
  }

  let confidence: CouncilConfidence = "low";
  if (confMatch) {
    const raw = confMatch[1].toLowerCase();
    if (raw === "high" || raw === "medium" || raw === "low") confidence = raw;
  }

  // Strip the header lines from the reasoning paragraph.
  const reasoning = text
    .replace(/^\s*STANCE\s*:\s*\S+\s*/im, "")
    .replace(/^\s*CONFIDENCE\s*:\s*\S+\s*/im, "")
    .trim();

  return { model: v.model, stance, confidence, reasoning: reasoning || text };
}

/**
 * Dissent threshold: any council member that DISAGREES with HIGH confidence.
 * Returns the dissenting verdicts (may be empty).
 */
export function selectDissent(verdicts: CouncilVerdict[]): ParsedVerdict[] {
  return verdicts
    .map(parseVerdict)
    .filter((p) => p.stance === "disagree" && p.confidence === "high");
}

// ---------------------------------------------------------------------------
// Preflight call to /api/council/consult
// ---------------------------------------------------------------------------

interface PreflightInput {
  /** The user's pending message. */
  message: string;
  /** Optional last-2-message context from the thread (each truncated upstream). */
  context?: string;
  /** Optional agent role/identity to give the council. */
  agentLabel?: string;
  /** Optional timeout in ms. Defaults to 8000. */
  timeoutMs?: number;
}

const PREFLIGHT_SYSTEM = `You are one of three independent advisors sitting on a council that reviews questions sent to a specialist AI agent. Your job is to flag bad questions, dangerous assumptions, or answers that would likely mislead the user — NOT to answer the question yourself.

You MUST format your reply with exactly two header lines, then 1-3 sentences of reasoning. No preamble, no markdown headers.

STANCE: AGREE | DISAGREE | UNCLEAR
CONFIDENCE: HIGH | MEDIUM | LOW
<reasoning>

Use STANCE: DISAGREE only when you believe the specialist agent should NOT proceed as asked — e.g. the question rests on a wrong premise, asks for something legally risky, or would lead to a clearly harmful answer. Use STANCE: AGREE if the question is reasonable and the agent should proceed. Use STANCE: UNCLEAR if you genuinely cannot tell.

Reserve CONFIDENCE: HIGH for cases where you are quite sure of your stance.`;

function buildPrompt(input: PreflightInput): string {
  const parts: string[] = [];
  if (input.agentLabel) {
    parts.push(`Specialist agent: ${input.agentLabel}`);
  }
  if (input.context && input.context.trim()) {
    parts.push(`Prior thread context (most recent):\n${input.context}`);
  }
  parts.push(`Question the user is about to send to the specialist agent:\n${input.message}`);
  parts.push(`Review the question above. Should the specialist agent proceed?`);
  return parts.join("\n\n");
}

/**
 * Fire a preflight council consult. Resolves with the raw verdicts list, or
 * an empty array on timeout/error (the caller treats both as "silent pass").
 *
 * NOT a React hook — exported as a plain async function so the AgentChat
 * submit handler can call it without hook-rules constraints.
 */
export async function runPreflightCouncil(input: PreflightInput): Promise<CouncilVerdict[]> {
  const timeoutMs = input.timeoutMs ?? 8000;
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);
  try {
    const resp = await fetch("/api/council/consult", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({
        prompt: buildPrompt(input),
        system: PREFLIGHT_SYSTEM,
      }),
      signal: controller.signal,
    });
    if (!resp.ok) return [];
    const data = (await resp.json()) as { verdicts?: CouncilVerdict[] };
    return Array.isArray(data.verdicts) ? data.verdicts : [];
  } catch {
    return [];
  } finally {
    clearTimeout(timer);
  }
}

// ---------------------------------------------------------------------------
// Toggle UI (header strip + dissent banner)
// ---------------------------------------------------------------------------

interface CouncilVetoToggleProps {
  on: boolean;
  onChange: (next: boolean) => void;
  /** Optional pending state — shows a small "Checking with council…" pill. */
  preflightInFlight?: boolean;
}

export function CouncilVetoToggle({ on, onChange, preflightInFlight }: CouncilVetoToggleProps) {
  return (
    <div className="flex items-center justify-between gap-2 px-8 py-2 border-t bg-background text-xs">
      <label className="inline-flex items-center gap-2 cursor-pointer select-none">
        <Switch
          checked={on}
          onCheckedChange={onChange}
          aria-label="Veto-check with LLM Council before answering"
        />
        <span className="text-muted-foreground">
          Veto-check with LLM Council before answering
        </span>
      </label>
      {on && preflightInFlight && (
        <Badge variant="info" className="text-[10px]">
          Checking with council…
        </Badge>
      )}
    </div>
  );
}

// ---------------------------------------------------------------------------
// Dissent banner (rendered above the in-flight assistant message)
// ---------------------------------------------------------------------------

interface CouncilDissentBannerProps {
  verdicts: CouncilVerdict[];
  /** Optional dismiss handler — when provided, renders an × button. */
  onDismiss?: () => void;
}

export function CouncilDissentBanner({ verdicts, onDismiss }: CouncilDissentBannerProps) {
  const [dismissed, setDismissed] = useState(false);
  const dissent = selectDissent(verdicts);
  if (dissent.length === 0 || dismissed) return null;

  function handleDismiss() {
    setDismissed(true);
    onDismiss?.();
  }

  return (
    <div className="max-w-3xl mx-auto rounded-md border border-amber-300 bg-amber-50 dark:border-amber-700 dark:bg-amber-950/40 p-4">
      <div className="flex items-center justify-between gap-2">
        <h3 className="text-sm font-semibold text-amber-900 dark:text-amber-100">
          ⚖️ Council dissent
        </h3>
        <button
          type="button"
          onClick={handleDismiss}
          aria-label="Dismiss council dissent"
          className="text-amber-900/70 dark:text-amber-100/70 hover:text-amber-900 dark:hover:text-amber-100 text-sm leading-none px-1"
        >
          ×
        </button>
      </div>
      <p className="mt-1 text-xs text-amber-900/80 dark:text-amber-100/80">
        {dissent.length === 1
          ? "1 council member raised a high-confidence objection to this question."
          : `${dissent.length} council members raised high-confidence objections to this question.`}
      </p>
      <div className="mt-3 grid gap-2">
        {dissent.map((d) => {
          const display = MODEL_DISPLAY[d.model];
          return (
            <Card key={d.model} className="bg-background/60 border-amber-200 dark:border-amber-800">
              <CardContent className="p-3 space-y-1.5">
                <div className="flex items-center justify-between gap-2 flex-wrap">
                  <div className="inline-flex items-center gap-2">
                    <span className="text-base">{display.icon}</span>
                    <span className="text-xs font-semibold">{display.label}</span>
                  </div>
                  <div className="inline-flex items-center gap-1">
                    <Badge variant="destructive" className="text-[10px] uppercase">
                      Disagree
                    </Badge>
                    <Badge variant="warning" className="text-[10px] uppercase">
                      High confidence
                    </Badge>
                  </div>
                </div>
                <p className="text-xs leading-relaxed whitespace-pre-wrap">{d.reasoning}</p>
              </CardContent>
            </Card>
          );
        })}
      </div>
    </div>
  );
}

// ---------------------------------------------------------------------------
// Convenience: build the "context" string from the last 2 thread messages.
// Each truncated to 1000 chars (per spec).
// ---------------------------------------------------------------------------

export function buildPreflightContext(
  history: Array<{ role: "user" | "assistant"; content: string }>,
): string {
  const last = history.slice(-2);
  return last
    .map((m) => {
      const content = m.content.length > 1000 ? `${m.content.slice(0, 1000)}…` : m.content;
      return `${m.role === "user" ? "User" : "Assistant"}: ${content}`;
    })
    .join("\n\n");
}
