"use client";

import { useAuth } from "@clerk/nextjs";
import { useCallback, useState } from "react";

function parseNdjsonChunk(
  buffer: string,
  onObject: (o: unknown) => void,
): string {
  const lines = buffer.split("\n");
  const rest = lines.pop() ?? "";
  for (const row of lines) {
    const t = row.trim();
    if (!t) continue;
    try {
      onObject(JSON.parse(t) as unknown);
    } catch {
      onObject({ error: `Bad JSON line: ${t.slice(0, 120)}` });
    }
  }
  return rest;
}

function isProgress(o: unknown): o is { kind: "progress"; message: string } {
  return (
    typeof o === "object" &&
    o !== null &&
    (o as { kind?: string }).kind === "progress" &&
    typeof (o as { message?: string }).message === "string"
  );
}

export function DigestDebugger() {
  const { userId: myId } = useAuth();
  const [userId, setUserId] = useState("");
  const [ignoreRedisHistory, setIgnoreRedisHistory] = useState(false);
  const [dryRunEmail, setDryRunEmail] = useState(false);
  const [force, setForce] = useState(false);
  const [skipCredits, setSkipCredits] = useState(true);
  const [busy, setBusy] = useState(false);
  const [logLines, setLogLines] = useState<string[]>([]);
  const [lastResult, setLastResult] = useState<unknown>(null);

  const append = useCallback((s: string) => {
    setLogLines((prev) => [...prev, s]);
  }, []);

  const runDigest = useCallback(async () => {
    const uid = userId.trim();
    if (!uid) {
      append("Enter a Clerk userId.");
      return;
    }
    setBusy(true);
    setLogLines([]);
    setLastResult(null);
    append(`POST /api/admin/trigger-digest (userId=${uid.slice(0, 12)}…)`);
    try {
      const res = await fetch("/api/admin/trigger-digest", {
        method: "POST",
        credentials: "same-origin",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          userId: uid,
          ignoreRedisHistory,
          dryRunEmail,
          force,
          skipCredits,
        }),
      });
      if (!res.ok || !res.body) {
        const text = await res.text();
        append(`HTTP ${res.status}: ${text.slice(0, 500)}`);
        return;
      }
      const reader = res.body.getReader();
      const dec = new TextDecoder();
      let buf = "";
      for (;;) {
        const { done, value } = await reader.read();
        if (done) break;
        buf += dec.decode(value, { stream: true });
        buf = parseNdjsonChunk(buf, (obj) => {
          if (
            typeof obj === "object" &&
            obj !== null &&
            "error" in obj &&
            typeof (obj as { error?: string }).error === "string"
          ) {
            append(`[error] ${(obj as { error: string }).error}`);
            return;
          }
          if (isProgress(obj)) {
            append(obj.message);
          } else if (
            typeof obj === "object" &&
            obj !== null &&
            (obj as { kind?: string }).kind === "classified" &&
            typeof (obj as { message?: string }).message === "string"
          ) {
            append(`→ ${(obj as { message: string }).message}`);
          } else if (
            typeof obj === "object" &&
            obj !== null &&
            (obj as { kind?: string }).kind === "error" &&
            typeof (obj as { message?: string }).message === "string"
          ) {
            append(`[throw] ${(obj as { message: string }).message}`);
          } else if (
            typeof obj === "object" &&
            obj !== null &&
            (obj as { kind?: string }).kind === "done"
          ) {
            const res = (obj as { result?: unknown }).result;
            setLastResult(res ?? null);
            append(`[done] ${JSON.stringify(res, null, 2)}`);
          }
        });
      }
      if (buf.trim()) {
        try {
          const o = JSON.parse(buf) as unknown;
          if (isProgress(o)) append(o.message);
          else if (
            typeof o === "object" &&
            o !== null &&
            (o as { kind?: string }).kind === "classified" &&
            typeof (o as { message?: string }).message === "string"
          )
            append(`→ ${(o as { message: string }).message}`);
          else if (
            typeof o === "object" &&
            o !== null &&
            (o as { kind?: string }).kind === "error" &&
            typeof (o as { message?: string }).message === "string"
          )
            append(`[throw] ${(o as { message: string }).message}`);
          else if (
            typeof o === "object" &&
            o !== null &&
            (o as { kind?: string }).kind === "done"
          ) {
            const res = (o as { result?: unknown }).result;
            setLastResult(res ?? null);
            append(`[done] ${JSON.stringify(res, null, 2)}`);
          }
        } catch {
          append(`[incomplete] ${buf.slice(0, 200)}`);
        }
      }
    } catch (e) {
      append(e instanceof Error ? e.message : String(e));
    } finally {
      setBusy(false);
    }
  }, [
    append,
    dryRunEmail,
    force,
    ignoreRedisHistory,
    skipCredits,
    userId,
  ]);

  return (
    <section
      style={{
        padding: 14,
        borderRadius: 10,
        border: "1px solid var(--border-subtle)",
        background: "var(--bg)",
      }}
    >
      <h2
        style={{
          fontSize: 11,
          fontWeight: 600,
          textTransform: "uppercase",
          letterSpacing: 0.5,
          color: "var(--muted)",
          marginBottom: 8,
        }}
      >
        Digest debugger
      </h2>
      <p style={{ fontSize: 12, color: "var(--muted)", marginBottom: 10 }}>
        Manually runs the same digest pipeline as QStash →{" "}
        <code style={{ fontSize: 11 }}>/api/internal/digest/process</code>.
        Set{" "}
        <code style={{ fontSize: 11 }}>DIGEST_TRACK_EMAILED_URLS=1</code> to
        dedupe emailed URLs in Redis; use “Ignore Redis history” to bypass that
        and inactive-engagement pause for repeat tests.
      </p>
      <div style={{ display: "flex", flexDirection: "column", gap: 8, marginBottom: 10 }}>
        <label style={{ fontSize: 13, display: "flex", flexDirection: "column", gap: 4 }}>
          Clerk user ID
          <input
            value={userId}
            onChange={(e) => setUserId(e.target.value)}
            placeholder={myId ?? "user_…"}
            spellCheck={false}
            style={{ fontFamily: "ui-monospace, monospace", fontSize: 12, padding: "6px 8px" }}
          />
        </label>
        <label style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13 }}>
          <input
            type="checkbox"
            checked={ignoreRedisHistory}
            onChange={(e) => setIgnoreRedisHistory(e.target.checked)}
          />
          Ignore Redis history (inactive pause + emailed-URL dedupe when enabled)
        </label>
        <label style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13 }}>
          <input
            type="checkbox"
            checked={dryRunEmail}
            onChange={(e) => setDryRunEmail(e.target.checked)}
          />
          Dry-run agent report (no Resend; refunds digest credit if charged)
        </label>
        <label style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13 }}>
          <input type="checkbox" checked={force} onChange={(e) => setForce(e.target.checked)} />
          Force run (ignore digest disabled / subscription gate)
        </label>
        <label style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13 }}>
          <input
            type="checkbox"
            checked={skipCredits}
            onChange={(e) => setSkipCredits(e.target.checked)}
          />
          Skip credit charge (recommended for tests)
        </label>
      </div>
      <button
        type="button"
        className="primary-btn"
        disabled={busy}
        onClick={() => void runDigest()}
      >
        {busy ? "Running…" : "Force run agent report"}
      </button>

      <div style={{ marginTop: 12 }}>
        <div
          style={{
            fontSize: 11,
            fontWeight: 600,
            color: "var(--muted)",
            marginBottom: 6,
          }}
        >
          Live log
        </div>
        <pre
          style={{
            margin: 0,
            padding: 10,
            maxHeight: 280,
            overflow: "auto",
            fontSize: 11,
            lineHeight: 1.45,
            background: "#111",
            color: "#b8c0cc",
            borderRadius: 8,
            whiteSpace: "pre-wrap",
            wordBreak: "break-word",
          }}
        >
          {logLines.length === 0 ? "(no output yet)" : logLines.join("\n")}
        </pre>
      </div>

      {lastResult !== null ? (
        <p style={{ fontSize: 11, color: "var(--muted)", marginTop: 8 }}>
          Last result object is included in the log as{" "}
          <code style={{ fontSize: 10 }}>[done]</code>.
        </p>
      ) : null}
    </section>
  );
}
