/**
 * FormsApp (Capture mode) — evidence-backed form layout (CE-WP-0003/0006/0007).
 *
 * Layout (CE-WP-0007):
 *
 *     ┌────────────┬─────────────────┬─────────────┐
 *     │ Collection │ ViewerShell     │ FormPane    │
 *     ├────────────┴─────────────────┴─────────────┤
 *     │ EvidenceStrip (bottom)                       │
 *     └────────────────────────────────────────────┘
 *
 * Linking: field must have focus; clicking evidence links directly.
 */

import { useCallback, useEffect, useMemo, useRef, useState } from "react";

import type { EvidenceItem } from "@shared/evidence";
import type { EvidenceLink } from "@shared/evidence-link";
import type { EvidenceItemId } from "@shared/ids";

import {
  Overlay,
  useActiveState,
  useBinder,
  useRegisterRect,
} from "@binder/index";
import type { FormFieldSchema, FormSchema } from "@binder/FormRenderer";
import {
  CollectionList,
  ViewerShell,
  useActiveDocument,
  useEngine,
  useEngineEventTick,
  useScrollToAnnotation,
} from "@work/index";

import { FormRenderer } from "@binder/FormRenderer";

import { DEMO_SCHEMA } from "./demo-schema";
import { HighlightRectBridge } from "./HighlightRectBridge";

export type EvidenceStripFilter = "all" | "attached";

const STRIP_FILTER_EVENT = "citation-evidence:strip-filter";

function publishStripFilter(mode: EvidenceStripFilter) {
  if (typeof window === "undefined") return;
  window.dispatchEvent(new CustomEvent(STRIP_FILTER_EVENT, { detail: mode }));
}

function quotePreview(text: string, max = 80): string {
  const t = text.trim();
  return t.length > max ? `${t.slice(0, max)}…` : t;
}

export function FormsApp() {
  const [schema, setSchema] = useState<FormSchema>(() => ({
    ...DEMO_SCHEMA,
    fields: [...DEMO_SCHEMA.fields],
  }));

  const fieldLabels = useMemo(
    () => new Map(schema.fields.map((f) => [f.id, f.label] as const)),
    [schema],
  );

  const addField = useCallback(() => {
    setSchema((prev) => {
      const n = prev.fields.length + 1;
      const field: FormFieldSchema = {
        type: "text",
        id: `field_${n}`,
        label: `New field ${n}`,
      };
      return { ...prev, fields: [...prev.fields, field] };
    });
  }, []);

  return (
    <div style={{ display: "flex", flexDirection: "column", height: "100%" }}>
      <div style={{ display: "flex", flex: 1, minHeight: 0 }}>
        <CollectionList />
        <ViewerShell />
        <FormPane schema={schema} onAddField={addField} />
      </div>
      <EvidenceStrip fieldLabels={fieldLabels} />
      <ScrollBridge />
      <HighlightRectBridge />
      <Overlay />
    </div>
  );
}

function ScrollBridge() {
  const { state } = useActiveState();
  const { scrollTo } = useScrollToAnnotation();
  useEffect(() => {
    if (state.activeAnnotationId) {
      scrollTo(state.activeAnnotationId);
    }
  }, [state.activeAnnotationId, scrollTo]);
  return null;
}

function FormPane({
  schema,
  onAddField,
}: {
  schema: FormSchema;
  onAddField: () => void;
}) {
  const { document } = useActiveDocument();
  const { bindings } = useBinder();
  const engine = useEngine();
  const linkTick = useEngineEventTick("EvidenceLinkCreated");
  const unlinkTick = useEngineEventTick("EvidenceLinkRemoved");
  const { state: activeState, setActiveEvidence } = useActiveState();

  useEffect(() => {
    return engine.bus.on("FormFieldActivated", () => {
      publishStripFilter("attached");
    });
  }, [engine]);

  useEffect(() => {
    const target = activeState.activeTarget;
    if (!target || activeState.activeEvidenceItemId) return;
    const links = bindings.listEvidenceForTarget(target);
    if (links.length === 0) return;
    const item = engine.evidence.get(links[0]!.evidenceItemId);
    if (!item) return;
    setActiveEvidence(item.id, item.annotationIds[0] ?? null);
  }, [
    activeState.activeTarget,
    activeState.activeEvidenceItemId,
    bindings,
    engine,
    linkTick,
    unlinkTick,
    setActiveEvidence,
  ]);

  const linkCounts = useMemo<Record<string, number>>(() => {
    const out: Record<string, number> = {};
    for (const field of schema.fields) {
      out[field.id] = bindings.listEvidenceForTarget({
        targetType: "form-field",
        targetId: field.id,
      }).length;
    }
    void linkTick;
    void unlinkTick;
    return out;
  }, [schema.fields, bindings, linkTick, unlinkTick]);

  const linkHints = useMemo<Record<string, string>>(() => {
    const out: Record<string, string> = {};
    for (const field of schema.fields) {
      const links = bindings.listEvidenceForTarget({
        targetType: "form-field",
        targetId: field.id,
      });
      if (links.length === 0) continue;
      const item = engine.evidence.get(links[0]!.evidenceItemId);
      const ann = item?.annotationIds[0]
        ? engine.annotations.get(item.annotationIds[0])
        : null;
      const quote = ann?.quote ?? item?.commentary ?? "";
      if (quote) out[field.id] = quotePreview(quote);
    }
    void linkTick;
    void unlinkTick;
    return out;
  }, [schema.fields, bindings, engine, linkTick, unlinkTick]);

  return (
    <main
      style={{
        flex: "0 0 320px",
        minWidth: 320,
        borderLeft: "1px solid #ddd",
        overflow: "auto",
        display: "flex",
        flexDirection: "column",
      }}
    >
      {document ? (
        <FormRenderer
          schema={schema}
          linkCounts={linkCounts}
          linkHints={linkHints}
          headerAction={
            <button
              type="button"
              data-testid="add-field-button"
              onClick={onAddField}
              style={{
                fontSize: 11,
                padding: "4px 10px",
                border: "1px solid #888",
                borderRadius: 4,
                background: "white",
                cursor: "pointer",
              }}
            >
              Add field
            </button>
          }
        />
      ) : (
        <EmptyHint />
      )}
    </main>
  );
}

function EmptyHint() {
  return (
    <p style={{ padding: 12, color: "#666", fontSize: 13, fontFamily: "system-ui, sans-serif" }}>
      Pick a document from the collection to start capturing evidence links.
    </p>
  );
}

function EvidenceStrip({
  fieldLabels,
}: {
  fieldLabels: ReadonlyMap<string, string>;
}) {
  const engine = useEngine();
  const { bindings } = useBinder();
  const { document } = useActiveDocument();
  const createTick = useEngineEventTick("EvidenceItemCreated");
  const updateTick = useEngineEventTick("EvidenceItemUpdated");
  const linkTick = useEngineEventTick("EvidenceLinkCreated");
  const unlinkTick = useEngineEventTick("EvidenceLinkRemoved");
  const { state: activeState, setActiveEvidence, clearActiveEvidence } =
    useActiveState();

  const [userFilter, setUserFilter] = useState<EvidenceStripFilter>("all");
  const [sessionFilter, setSessionFilter] = useState<EvidenceStripFilter | null>(
    null,
  );

  const effectiveFilter = sessionFilter ?? userFilter;

  useEffect(() => {
    const handler = (e: Event) => {
      setSessionFilter((e as CustomEvent<EvidenceStripFilter>).detail);
    };
    window.addEventListener(STRIP_FILTER_EVENT, handler);
    return () => window.removeEventListener(STRIP_FILTER_EVENT, handler);
  }, []);

  useEffect(() => {
    if (!activeState.activeTarget) {
      setSessionFilter(null);
    }
  }, [activeState.activeTarget]);

  const allItems = useMemo<readonly EvidenceItem[]>(() => {
    if (!document) return [];
    void createTick;
    void updateTick;
    void linkTick;
    void unlinkTick;
    return engine.evidence.listByDocument(document.id);
  }, [document, engine, createTick, updateTick, linkTick, unlinkTick]);

  const items = useMemo(() => {
    if (effectiveFilter !== "attached" || !activeState.activeTarget) {
      return allItems;
    }
    const links = bindings.listEvidenceForTarget(activeState.activeTarget);
    const ids = new Set(links.map((l) => l.evidenceItemId));
    const attached = allItems.filter((item) => ids.has(item.id));
    return attached.length > 0 ? attached : allItems;
  }, [
    allItems,
    effectiveFilter,
    activeState.activeTarget,
    bindings,
    linkTick,
    unlinkTick,
  ]);

  const tryLink = useCallback(
    (evidenceItemId: EvidenceItemId, fieldId: string): boolean => {
      const existing = bindings
        .listEvidenceForTarget({ targetType: "form-field", targetId: fieldId })
        .some((l) => l.evidenceItemId === evidenceItemId);
      if (existing) return false;
      bindings.linkEvidenceToTarget({
        evidenceItemId,
        target: { targetType: "form-field", targetId: fieldId },
      });
      return true;
    },
    [bindings],
  );

  const handleCardClick = useCallback(
    (item: EvidenceItem) => {
      const annId = item.annotationIds[0] ?? null;
      setActiveEvidence(item.id, annId);

      const target = activeState.activeTarget;
      if (target?.targetType === "form-field") {
        tryLink(item.id, target.targetId);
      }
    },
    [activeState.activeTarget, setActiveEvidence, tryLink],
  );

  const handleUnlink = useCallback(
    (link: EvidenceLink) => {
      bindings.unlinkEvidence(link.id);
      if (
        activeState.activeEvidenceItemId === link.evidenceItemId &&
        activeState.activeTarget?.targetType === link.targetType &&
        activeState.activeTarget?.targetId === link.targetId
      ) {
        clearActiveEvidence();
      }
    },
    [bindings, activeState, clearActiveEvidence],
  );

  if (!document) return null;

  return (
    <section
      aria-label="Evidence list"
      style={{
        borderTop: "1px solid #ddd",
        background: "#fafafa",
        padding: 8,
        display: "flex",
        flexDirection: "column",
        gap: 6,
        flex: "0 0 auto",
        minHeight: 100,
        fontFamily: "system-ui, sans-serif",
      }}
    >
      <div style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 11 }}>
        <span style={{ color: "#666" }}>Show:</span>
        <FilterToggle
          label="All"
          active={effectiveFilter === "all"}
          onClick={() => {
            setUserFilter("all");
            setSessionFilter(null);
          }}
        />
        <FilterToggle
          label="Linked to field"
          active={effectiveFilter === "attached"}
          onClick={() => {
            setUserFilter("attached");
            setSessionFilter(null);
          }}
        />
      </div>
      <div style={{ display: "flex", gap: 8, overflowX: "auto" }}>
        {items.length === 0 && (
          <p style={{ fontSize: 12, color: "#888", margin: 0, alignSelf: "center" }}>
            {effectiveFilter === "attached"
              ? "No evidence linked to the active field."
              : "No evidence yet. Switch to Review mode to capture a passage."}
          </p>
        )}
        {items.map((item) => (
          <EvidenceStripCard
            key={item.id}
            item={item}
            isActive={activeState.activeEvidenceItemId === item.id}
            links={bindings.listTargetsForEvidence(item.id)}
            fieldLabels={fieldLabels}
            onClick={() => handleCardClick(item)}
            onUnlink={handleUnlink}
          />
        ))}
      </div>
    </section>
  );
}

function FilterToggle({
  label,
  active,
  onClick,
}: {
  label: string;
  active: boolean;
  onClick: () => void;
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      aria-pressed={active}
      style={{
        fontSize: 11,
        padding: "2px 8px",
        borderRadius: 4,
        border: active ? "1px solid #0050b3" : "1px solid #ccc",
        background: active ? "#e8f0ff" : "white",
        cursor: "pointer",
      }}
    >
      {label}
    </button>
  );
}

function EvidenceStripCard({
  item,
  isActive,
  links,
  fieldLabels,
  onClick,
  onUnlink,
}: {
  item: EvidenceItem;
  isActive: boolean;
  links: readonly EvidenceLink[];
  fieldLabels: ReadonlyMap<string, string>;
  onClick: () => void;
  onUnlink: (link: EvidenceLink) => void;
}) {
  const engine = useEngine();
  const ref = useRef<HTMLDivElement>(null);
  useRegisterRect("evidence-card", item.id, ref);

  const firstAnn = item.annotationIds[0]
    ? engine.annotations.get(item.annotationIds[0])
    : null;
  const quote = firstAnn?.quote ?? "(no quote)";

  const formLinks = links.filter((l) => l.targetType === "form-field");

  return (
    <div
      ref={ref}
      style={{
        position: "relative",
        minWidth: 220,
        maxWidth: 280,
        flexShrink: 0,
      }}
    >
      {formLinks.length > 0 && (
        <div
          style={{
            position: "absolute",
            top: 4,
            right: 4,
            display: "flex",
            gap: 2,
            zIndex: 1,
          }}
        >
          {formLinks.map((link) => {
            const label = fieldLabels.get(link.targetId) ?? link.targetId;
            return (
              <button
                key={link.id}
                type="button"
                title={`Linked to: ${label}. Click to remove link.`}
                aria-label={`Remove link to ${label}`}
                onClick={(e) => {
                  e.stopPropagation();
                  onUnlink(link);
                }}
                style={{
                  fontSize: 10,
                  lineHeight: 1,
                  padding: "2px 4px",
                  border: "1px solid #88a",
                  borderRadius: 3,
                  background: "#eef",
                  cursor: "pointer",
                }}
              >
                ⧉
              </button>
            );
          })}
        </div>
      )}
      <button
        type="button"
        onClick={onClick}
        aria-current={isActive ? "true" : undefined}
        style={{
          width: "100%",
          textAlign: "left",
          fontSize: 12,
          padding: 8,
          border: isActive ? "2px solid #0050b3" : "1px solid #ccc",
          background: isActive ? "#e8f0ff" : "white",
          cursor: "pointer",
        }}
      >
        <div
          style={{
            fontStyle: "italic",
            marginBottom: 4,
            paddingRight: formLinks.length ? 24 : 0,
          }}
        >
          &ldquo;{quote.slice(0, 100)}
          {quote.length > 100 ? "…" : ""}&rdquo;
        </div>
        {item.commentary && <div style={{ color: "#333" }}>{item.commentary}</div>}
      </button>
    </div>
  );
}