import { useEffect, useState } from "preact/hooks";
import { decodeJsonError } from "../../cardRuntime/api";
import { requestCardFeedRefresh } from "../../cardRuntime/store";

const INBOX_REFRESH_EVENT = "nanobot:inbox-refresh";

interface InboxItem {
  path: string;
  title: string;
  kind: string;
  status: string;
  source: string;
  confidence: number | null;
  captured: string;
  updated: string;
  suggestedDue: string;
  tags: string[];
  body: string;
}

function normalizeTaskTag(raw: string): string {
  const trimmed = raw.trim().replace(/^#+/, "").replace(/\s+/g, "-");
  return trimmed ? `#${trimmed}` : "";
}

function normalizeTaskTags(raw: unknown): string[] {
  if (!Array.isArray(raw)) return [];
  const seen = new Set<string>();
  const tags: string[] = [];
  for (const value of raw) {
    const tag = normalizeTaskTag(String(value || ""));
    const key = tag.toLowerCase();
    if (!tag || seen.has(key)) continue;
    seen.add(key);
    tags.push(tag);
  }
  return tags;
}

function normalizeInboxItem(raw: unknown): InboxItem | null {
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
  const record = raw as Record<string, unknown>;
  const path = typeof record.path === "string" ? record.path.trim() : "";
  if (!path) return null;
  return {
    path,
    title:
      typeof record.title === "string" && record.title.trim()
        ? record.title.trim()
        : "Untitled capture",
    kind: typeof record.kind === "string" ? record.kind.trim() : "unknown",
    status: typeof record.status === "string" ? record.status.trim() : "new",
    source: typeof record.source === "string" ? record.source.trim() : "unknown",
    confidence:
      typeof record.confidence === "number" && Number.isFinite(record.confidence)
        ? record.confidence
        : null,
    captured: typeof record.captured === "string" ? record.captured.trim() : "",
    updated: typeof record.updated === "string" ? record.updated.trim() : "",
    suggestedDue: typeof record.suggested_due === "string" ? record.suggested_due.trim() : "",
    tags: normalizeTaskTags(record.tags),
    body: typeof record.body === "string" ? record.body : "",
  };
}

async function fetchInboxItems(limit = 4): Promise<InboxItem[]> {
  const params = new URLSearchParams({ limit: String(limit) });
  const resp = await fetch(`/inbox?${params.toString()}`, { cache: "no-store" });
  if (!resp.ok) throw new Error(await decodeJsonError(resp));
  const payload = (await resp.json()) as { items?: unknown };
  const items = Array.isArray(payload.items) ? payload.items : [];
  return items.map(normalizeInboxItem).filter((item): item is InboxItem => item !== null);
}

function requestInboxRefresh(): void {
  window.dispatchEvent(new Event(INBOX_REFRESH_EVENT));
}

function extractCaptureTags(text: string): string[] {
  const matches = text.match(/(^|\s)#([A-Za-z0-9_-]+)/g) ?? [];
  return Array.from(
    new Set(
      matches
        .map((match) => match.trim())
        .filter(Boolean)
        .map((tag) => normalizeTaskTag(tag)),
    ),
  );
}

export async function captureInboxItem(text: string): Promise<InboxItem> {
  const trimmed = text.trim();
  if (!trimmed) throw new Error("capture text is required");
  const resp = await fetch("/inbox/capture", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      text: trimmed,
      tags: extractCaptureTags(trimmed),
      source: "web-ui",
    }),
  });
  if (!resp.ok) throw new Error(await decodeJsonError(resp));
  const payload = (await resp.json()) as { item?: unknown };
  const item = normalizeInboxItem(payload.item);
  if (!item) throw new Error("invalid inbox response");
  requestInboxRefresh();
  return item;
}

async function dismissInboxItem(itemPath: string): Promise<void> {
  const resp = await fetch("/inbox/dismiss", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ item: itemPath }),
  });
  if (!resp.ok) throw new Error(await decodeJsonError(resp));
  requestInboxRefresh();
}

async function acceptInboxItemAsTask(itemPath: string): Promise<void> {
  const resp = await fetch("/inbox/accept-task", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ item: itemPath, lane: "backlog" }),
  });
  if (!resp.ok) throw new Error(await decodeJsonError(resp));
  requestInboxRefresh();
  requestCardFeedRefresh();
}

function formatInboxUpdatedAt(timestamp: string): string {
  const parsed = new Date(timestamp);
  if (Number.isNaN(parsed.getTime())) return "";
  return parsed.toLocaleString([], {
    month: "short",
    day: "numeric",
    hour: "numeric",
    minute: "2-digit",
  });
}

function summarizeInboxBody(body: string): string {
  return body.replace(/\r\n?/g, "\n").split("\n## Raw Capture")[0].replace(/\s+/g, " ").trim();
}

function computeInboxScore(items: InboxItem[]): number {
  if (!items.length) return 0;
  const newest = items[0];
  const newestMs = newest.updated ? new Date(newest.updated).getTime() : Number.NaN;
  const ageHours = Number.isFinite(newestMs) ? (Date.now() - newestMs) / (60 * 60 * 1000) : 999;
  if (ageHours <= 1) return 91;
  if (ageHours <= 6) return 86;
  if (ageHours <= 24) return 82;
  return 78;
}

export function InboxQuickAdd({
  onSubmit,
  visible,
}: {
  onSubmit(text: string): Promise<void>;
  visible: boolean;
}) {
  const [open, setOpen] = useState(false);
  const [value, setValue] = useState("");
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState("");

  useEffect(() => {
    if (!visible) {
      setOpen(false);
      setError("");
    }
  }, [visible]);

  const close = () => {
    if (busy) return;
    setOpen(false);
    setError("");
  };

  const submit = async () => {
    const trimmed = value.trim();
    if (!trimmed) return;
    setBusy(true);
    setError("");
    try {
      await onSubmit(trimmed);
      setValue("");
      setOpen(false);
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    } finally {
      setBusy(false);
    }
  };

  if (!visible) return null;

  return (
    <>
      <button
        id="inbox-quick-add-btn"
        type="button"
        data-no-swipe="1"
        aria-label="Quick add to inbox"
        onClick={() => setOpen(true)}
      >
        +
      </button>
      {open ? (
        <>
          <button
            id="inbox-quick-add-backdrop"
            type="button"
            data-no-swipe="1"
            aria-label="Close inbox quick add"
            onClick={close}
          />
          <div id="inbox-quick-add-sheet" data-no-swipe="1">
            <div class="inbox-quick-add-sheet__title">Quick Add</div>
            <textarea
              class="inbox-quick-add-sheet__input"
              rows={3}
              placeholder="Something to remember..."
              value={value}
              disabled={busy}
              onInput={(event) => setValue(event.currentTarget.value)}
            />
            {error ? <div class="inbox-quick-add-sheet__error">{error}</div> : null}
            <div class="inbox-quick-add-sheet__actions">
              <button
                class="inbox-quick-add-sheet__btn"
                type="button"
                disabled={busy}
                onClick={close}
              >
                Cancel
              </button>
              <button
                class="inbox-quick-add-sheet__btn primary"
                type="button"
                disabled={busy || !value.trim()}
                onClick={() => {
                  void submit();
                }}
              >
                Add
              </button>
            </div>
          </div>
        </>
      ) : null}
    </>
  );
}

export function InboxReviewCard() {
  const [items, setItems] = useState<InboxItem[]>([]);
  const [loading, setLoading] = useState(true);
  const [busyItem, setBusyItem] = useState<{ path: string; action: "accept" | "dismiss" } | null>(
    null,
  );
  const [errorText, setErrorText] = useState("");

  const loadInbox = async () => {
    setLoading(true);
    try {
      const nextItems = await fetchInboxItems(4);
      setItems(nextItems);
      setErrorText("");
    } catch (error) {
      console.error("Inbox load failed", error);
      setErrorText(error instanceof Error ? error.message : String(error));
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    void loadInbox();
    const onRefresh = (event?: Event) => {
      const customEvent = event as CustomEvent<{ items?: unknown[] }> | undefined;
      const nextItems = customEvent?.detail?.items;
      if (Array.isArray(nextItems)) {
        setItems(
          nextItems.map(normalizeInboxItem).filter((item): item is InboxItem => item !== null),
        );
        setLoading(false);
        setErrorText("");
        return;
      }
      void loadInbox();
    };
    window.addEventListener(INBOX_REFRESH_EVENT, onRefresh);
    window.addEventListener("focus", onRefresh);
    return () => {
      window.removeEventListener(INBOX_REFRESH_EVENT, onRefresh);
      window.removeEventListener("focus", onRefresh);
    };
  }, []);

  const withBusyItem = async (
    itemPath: string,
    actionKind: "accept" | "dismiss",
    action: () => Promise<void>,
  ) => {
    setBusyItem({ path: itemPath, action: actionKind });
    setErrorText("");
    try {
      await action();
      await loadInbox();
    } catch (error) {
      console.error("Inbox action failed", error);
      setErrorText(error instanceof Error ? error.message : String(error));
    } finally {
      setBusyItem(null);
    }
  };

  const score = computeInboxScore(items);

  if (!loading && !errorText && items.length === 0) {
    return null;
  }

  return (
    <article class="card kind-text state-active inbox-review-card">
      <header class="card-header">
        <div class="card-title-wrap">
          <div class="card-title-line">
            <span class="card-title">Inbox</span>
          </div>
          <div class="card-meta">
            <span class="card-state state-active">
              {loading ? "Loading" : `${items.length} open`}
            </span>
          </div>
        </div>
      </header>
      <div class="inbox-review-card__body">
        <div class="inbox-review-card__summary">
          Capture first, organize later.
          {score ? <span class="inbox-review-card__score">Priority {score}</span> : null}
        </div>
        {errorText ? <div class="inbox-review-card__error">{errorText}</div> : null}
        {items.map((item) => {
          const preview = summarizeInboxBody(item.body);
          const itemBusy = busyItem?.path === item.path;
          const accepting = busyItem?.path === item.path && busyItem.action === "accept";
          return (
            <div key={item.path} class="inbox-review-card__item">
              <div class="inbox-review-card__item-topline">
                <span class="inbox-review-card__item-kind">{item.kind}</span>
                <span class={`inbox-review-card__item-updated${accepting ? " is-active" : ""}`}>
                  {accepting ? "Nanobot..." : formatInboxUpdatedAt(item.updated || item.captured)}
                </span>
              </div>
              <div class="inbox-review-card__item-title">{item.title}</div>
              {preview ? <div class="inbox-review-card__item-preview">{preview}</div> : null}
              {item.tags.length ? (
                <div class="inbox-review-card__tags">
                  {item.tags.map((tag) => (
                    <span key={tag} class="inbox-review-card__tag">
                      {tag}
                    </span>
                  ))}
                </div>
              ) : null}
              <div class="inbox-review-card__actions">
                <button
                  class="inbox-review-card__action"
                  type="button"
                  disabled={itemBusy}
                  onClick={() => {
                    void withBusyItem(item.path, "accept", () => acceptInboxItemAsTask(item.path));
                  }}
                >
                  {accepting ? "Nanobot..." : "Ask Nanobot"}
                </button>
                <button
                  class="inbox-review-card__action ghost"
                  type="button"
                  disabled={itemBusy}
                  onClick={() => {
                    void withBusyItem(item.path, "dismiss", () => dismissInboxItem(item.path));
                  }}
                >
                  Dismiss
                </button>
              </div>
            </div>
          );
        })}
      </div>
    </article>
  );
}
