import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { apiRequest } from "./client";

export type OpsService = {
  name: string;
  status: string;
  restarts: number;
  uptime_ms: number;
  cpu: number;
  memory_bytes: number;
  cron: string;
  autorestart: boolean;
  disabled?: boolean; // cron drain switched off via the dashboard
  burst_until?: string | null; // manual "run continuously now" window (ISO) — see useDrainBurst
};

export type CourtFetchJob = {
  case_number_norm: string;
  citation_raw: string;
  tier: string;
  status: string;
  error: string;
  updated_at: string;
};

export type IngestedRow = {
  case_number: string;
  court: string;
  source_url: string;
  created_at: string;
};

/** The uniform per-pipeline shape every background drain reports. */
export type PipelineStats = {
  pending: number; // awaiting processing (status='pending') — not necessarily in the active queue
  processing: number; // being worked right now
  done: number; // completed
  failed: number; // terminal failures (court_fetch folds in 'manual')
  queued: number; // explicitly enqueued for the next drain run
  running_now: string[]; // human labels of the items currently processing
  by_status: Record<string, number>; // raw counts, for the curious
};

/** One claude.ai usage window (5-hour / weekly / weekly-per-model). */
export type UsageWindow = {
  utilization: number | null; // 0-100; null when the window is inactive
  resets_at: string | null;
};

/** claude.ai subscription usage — the same %s the Claude Code status bar shows,
 *  via the (undocumented) OAuth usage endpoint proxied by the host bridge.
 *  null when the endpoint is unreachable. */
export type SubscriptionUsage = {
  five_hour?: UsageWindow | null;
  seven_day?: UsageWindow | null;
  seven_day_opus?: UsageWindow | null;
  seven_day_sonnet?: UsageWindow | null;
} | null;

export type OperationsSnapshot = {
  services: OpsService[];
  services_error: string | null;
  subscription_usage: SubscriptionUsage;
  pipelines: {
    court_fetch: PipelineStats & { recent: CourtFetchJob[] };
    metadata_extraction: PipelineStats;
    halacha_extraction: PipelineStats;
    digests: PipelineStats & { total: number; linked: number };
    halacha_review: { by_status: Record<string, number> };
    missing_precedents: { by_status: Record<string, number> };
    ingested_recent: IngestedRow[];
  };
};

export function useOperations() {
  return useQuery({
    queryKey: ["operations"],
    queryFn: ({ signal }) =>
      apiRequest<OperationsSnapshot>("/api/operations", { signal }),
    refetchInterval: 5000, // live view of background work
    staleTime: 3000,
  });
}

export type ServiceAction = "restart" | "stop" | "start" | "run-now";

/** Control a background service (daemon restart/stop/start, or run a drain now). */
export function useServiceAction() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: ({ name, action }: { name: string; action: ServiceAction }) =>
      apiRequest(`/api/operations/services/${name}/${action}`, { method: "POST" }),
    onSuccess: (_d, { action }) => {
      const labels: Record<ServiceAction, string> = {
        "run-now": "הופעל עכשיו",
        restart: "הופעל מחדש",
        stop: "נעצר",
        start: "הופעל",
      };
      toast.success(labels[action]);
      qc.invalidateQueries({ queryKey: ["operations"] });
    },
    onError: (e) => toast.error(`הפעולה נכשלה: ${String(e)}`),
  });
}

/** Switch a cron drain on/off (its "startup type"). */
export function useDrainToggle() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: ({ name, disabled }: { name: string; disabled: boolean }) =>
      apiRequest(`/api/operations/drains/${name}/disabled`, {
        method: "POST",
        body: { disabled },
      }),
    onSuccess: (_d, { disabled }) => {
      toast.success(disabled ? "התזמון כובה" : "התזמון הופעל");
      qc.invalidateQueries({ queryKey: ["operations"] });
    },
    onError: (e) => toast.error(`העדכון נכשל: ${String(e)}`),
  });
}

/**
 * Start/stop a drain's MANUAL burst window ("run continuously now until X").
 * `until` is a tz-naive local datetime string (e.g. "2026-06-13T18:00") — the
 * backend interprets it as Israel time. Omit it to let the server default to the
 * upcoming Saturday 18:00. The host supervisor enforces it within ≤15 min.
 */
export function useDrainBurst() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: ({ name, action, until }: { name: string; action: "on" | "off"; until?: string }) =>
      apiRequest(`/api/operations/drains/${name}/burst`, {
        method: "POST",
        body: action === "on" ? { action, ...(until ? { until } : {}) } : { action },
      }),
    onSuccess: (_d, { action }) => {
      toast.success(action === "on" ? "BURST הופעל" : "BURST כובה");
      qc.invalidateQueries({ queryKey: ["operations"] });
    },
    onError: (e) => toast.error(`הפעולה נכשלה: ${String(e)}`),
  });
}

// ── Live agents — which agent is working now + its output + controls ───────

export type AgentRun = {
  run_id: string;
  agent_id: string;
  agent_name: string;
  company_id: string;
  company_label: string;
  status: string; // running | queued | ...
  invocation_source: string;
  trigger_detail: string;
  issue_id: string | null;
  adapter_type: string;
  started_at: string | null;
  created_at: string | null;
  last_output_at: string | null;
  continuation_attempt: number;
  silence_level: string; // "" | ok | suspicion | critical
  silence_age_ms: number;
};

export type AgentRunsResponse = {
  runs: AgentRun[];
  running: number;
  queued: number;
  errors: string[];
};

export type RunLog = {
  runId: string;
  store: string;
  logRef: string;
  content: string; // NDJSON stream the adapter captured
};

/** Queued + running heartbeat runs across all companies. */
export function useAgentRuns() {
  return useQuery({
    queryKey: ["operations", "agents"],
    queryFn: ({ signal }) =>
      apiRequest<AgentRunsResponse>("/api/operations/agents", { signal }),
    refetchInterval: 4000, // live view of who's working now
    staleTime: 2000,
  });
}

// ── Agent health taxonomy (#222) + recent escalations (#218) ───────────────
export type AgentHealthState = "zombie" | "stalled" | "working" | "idle";

export type AgentHealthItem = {
  issue_id: string;
  identifier: string;
  status: string;
  agent_id: string;
  agent_name: string;
  health: AgentHealthState;
  wakeups: number;
  recovery_wakeups: number;
};

export type AgentHealthResponse = {
  ok: boolean;
  items: AgentHealthItem[]; // worst-first (zombie → stalled → working → idle)
  counts: Record<AgentHealthState, number>;
};

/** Per-issue agent health — surfaces recovery-loop zombies automatically. */
export function useAgentHealth() {
  return useQuery({
    queryKey: ["operations", "agents", "health"],
    queryFn: ({ signal }) =>
      apiRequest<AgentHealthResponse>("/api/operations/agents/health", { signal }),
    refetchInterval: 5000, // live alongside the running-agents view
    staleTime: 3000,
  });
}

export type EscalationItem = {
  issue_id: string;
  identifier: string;
  severity: "critical" | "high" | "medium";
  reason: string;
  created_at: string | null;
};

export type RecentEscalationsResponse = {
  ok: boolean;
  items: EscalationItem[]; // newest-first
};

/** Recent chair escalations (watchdog + manual) — the handled-loop history. */
export function useRecentEscalations() {
  return useQuery({
    queryKey: ["operations", "agents", "escalations"],
    queryFn: ({ signal }) =>
      apiRequest<RecentEscalationsResponse>("/api/operations/agents/escalations", { signal }),
    refetchInterval: 15000, // escalations change slowly (watchdog runs every 15m)
    staleTime: 10000,
  });
}

/** Full output log of one run — fetched on demand (drawer open). */
export function useRunLog(runId: string | null) {
  return useQuery({
    queryKey: ["operations", "agents", "log", runId],
    queryFn: ({ signal }) =>
      apiRequest<RunLog>(`/api/operations/agents/runs/${runId}/log`, { signal }),
    enabled: !!runId,
    refetchInterval: runId ? 4000 : false, // live tail while open
  });
}

/** Gracefully cancel a queued/running run (not a raw kill). */
export function useCancelRun() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (runId: string) =>
      apiRequest(`/api/operations/agents/runs/${runId}/cancel`, { method: "POST" }),
    onSuccess: () => {
      toast.success("בקשת עצירה נשלחה");
      qc.invalidateQueries({ queryKey: ["operations", "agents"] });
    },
    onError: (e) => toast.error(`העצירה נכשלה: ${String(e)}`),
  });
}

/** Reset a wedged agent session so its next wakeup starts clean. */
export function useResetAgentSession() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (agentId: string) =>
      apiRequest(`/api/operations/agents/${agentId}/reset-session`, { method: "POST" }),
    onSuccess: () => {
      toast.success("ה-session אופס");
      qc.invalidateQueries({ queryKey: ["operations", "agents"] });
    },
    onError: (e) => toast.error(`האיפוס נכשל: ${String(e)}`),
  });
}

// ── Agent adapter migration ────────────────────────────────────────────────
// Migrate an agent (or "all") between run-engines (claude_local / gemini_local /
// deepseek_local / codex_local) in BOTH companies. Host-side (runs scripts/migrate_agent_adapter.py
// via the court-fetch bridge), so the script's exit code + output are relayed so
// the panel can render preflight warnings. A non-zero exit on a "check" is an
// informative refusal, not a transport error — callers inspect exit_code.
export type MigrateAction = "check" | "apply" | "revert" | "verify";

export type MigrateAdapterRequest = {
  action: MigrateAction;
  agent?: string; // agent display-name, or "all"
  to?: string; // target adapter (for check/apply)
  model?: string;
  relax_tools?: boolean;
};

export type MigrateAdapterResult = {
  ok: boolean;
  exit_code: number;
  stdout: string;
  stderr: string;
};

/** Run an adapter migration action on the host bridge. Caller handles toasts —
 *  the meaning of the result depends on the action (preflight vs apply vs verify). */
export function useMigrateAdapter() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (body: MigrateAdapterRequest) =>
      apiRequest<MigrateAdapterResult>("/api/operations/agents/migrate-adapter", {
        method: "POST",
        body,
      }),
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ["settings", "paperclip-agents"] });
      qc.invalidateQueries({ queryKey: ["operations", "agents"] });
    },
  });
}
