import { useEffect, useMemo, useState, type FormEvent } from "react";
import { Link, useSearchParams } from "react-router-dom";
import { useQuery } from "@tanstack/react-query";
import { FileBox, FolderTree, Server } from "lucide-react";
import { lookupVrps, searchRun } from "../api/service";
import type { VrpRecord } from "../api/schemas";
import { CursorPagerControls } from "../components/CursorPagerControls";
import { DataTable, type Column } from "../components/DataTable";
import { PageHeader } from "../components/PageHeader";
import { Panel } from "../components/Panel";
import { StatusPill } from "../components/StatusPill";
import { EmptyBlock, ErrorBlock, Notice } from "../components/StateBlock";
import { useCursorPager } from "../lib/cursor";
import { objectTypeLabel } from "../lib/format";
import { withRunParam } from "../lib/run";
import { useRun, runErrorTitle } from "../lib/useRun";
import { classifyNetworkQuery, parseAsn, type NetworkQuery } from "../lib/vrpQuery";

const VRP_PAGE_SIZE = 50;

/**
 * VRP lookup results for IP / CIDR queries, backed by
 * `GET /runs/{id}/vrps/lookup` (backend feature #122). Optional ASN filter
 * rides in the `?asn=` URL param so results stay deep-linkable.
 */
function VrpResults({ network }: { network: NetworkQuery & { kind: "ip" | "prefix" } }) {
  const { runId } = useRun();
  const [searchParams, setSearchParams] = useSearchParams();
  const asnParam = searchParams.get("asn") ?? "";
  const asn = asnParam ? parseAsn(asnParam) : null;
  const [asnDraft, setAsnDraft] = useState(asnParam);
  const [asnError, setAsnError] = useState<string | null>(null);
  const pager = useCursorPager(`vrp|${runId}|${network.kind}|${network.kind === "ip" ? network.ip : network.prefix}|${asn ?? ""}`);

  useEffect(() => setAsnDraft(asnParam), [asnParam]);

  const target = network.kind === "ip" ? { ip: network.ip } : { prefix: network.prefix };
  const vrpQuery = useQuery({
    queryKey: ["vrp-lookup", runId, target, asn, pager.cursor],
    queryFn: () =>
      lookupVrps(runId, { ...target, asn: asn ?? undefined, limit: VRP_PAGE_SIZE, cursor: pager.cursor }),
    placeholderData: (prev) => prev,
  });

  const applyAsnFilter = (event: FormEvent) => {
    event.preventDefault();
    const draft = asnDraft.trim();
    if (!draft) {
      setAsnError(null);
      const params = new URLSearchParams(searchParams);
      params.delete("asn");
      setSearchParams(params, { preventScrollReset: true });
      return;
    }
    const parsed = parseAsn(draft);
    if (parsed === null) {
      setAsnError(`“${draft}” is not a valid ASN (digits, optionally AS-prefixed).`);
      return;
    }
    setAsnError(null);
    const params = new URLSearchParams(searchParams);
    params.set("asn", String(parsed));
    setSearchParams(params, { preventScrollReset: true });
  };

  const columns: Column<VrpRecord>[] = [
    {
      key: "asn",
      header: "ASN",
      render: (vrp) => <span className="cell-main mono">AS{vrp.asn}</span>,
    },
    {
      key: "prefix",
      header: "Prefix",
      render: (vrp) => <span className="mono">{vrp.prefix}</span>,
    },
    {
      key: "maxLength",
      header: "Max length",
      numeric: true,
      render: (vrp) => <span className="mono">/{vrp.maxLength}</span>,
    },
  ];

  const targetLabel = network.kind === "ip" ? network.ip : network.prefix;
  const emptyHint =
    network.kind === "prefix"
      ? "Covering semantics: a VRP only matches when its prefix covers the whole queried prefix and its maxLength is at least the queried length."
      : "No VRP prefix covers this address in the active run.";

  return (
    <Panel
      title="VRP lookup"
      subtitle={
        network.kind === "ip"
          ? `VRPs whose prefix covers ${targetLabel}`
          : `VRPs covering the whole prefix ${targetLabel} (maxLength ≥ queried length)`
      }
      tools={
        <form className="vrp-asn-filter" onSubmit={applyAsnFilter} role="search">
          <label htmlFor="vrp-asn">ASN filter</label>
          <input
            id="vrp-asn"
            type="text"
            inputMode="numeric"
            value={asnDraft}
            onChange={(e) => setAsnDraft(e.target.value)}
            placeholder="e.g. AS45090 (optional)"
          />
          <button type="submit" className="btn small">
            Apply
          </button>
        </form>
      }
    >
      {asnError ? <Notice kind="warning">{asnError}</Notice> : null}
      <DataTable
        columns={columns}
        rows={vrpQuery.data?.items ?? []}
        rowKey={(vrp) => `${vrp.asn}-${vrp.prefix}-${vrp.maxLength}`}
        loading={vrpQuery.isPending}
        error={vrpQuery.isError ? vrpQuery.error : undefined}
        onRetry={() => vrpQuery.refetch()}
        emptyTitle={`No VRPs cover ${targetLabel}${asn !== null ? ` for AS${asn}` : ""}`}
        emptyHint={emptyHint}
        caption={`VRP lookup results for ${targetLabel}`}
      />
      <CursorPagerControls
        page={pager.page}
        canPrev={pager.canPrev}
        hasNext={Boolean(vrpQuery.data?.nextCursor)}
        loading={vrpQuery.isFetching}
        onPrev={pager.goPrev}
        onNext={() => pager.goNext(vrpQuery.data?.nextCursor ?? null)}
        itemCount={vrpQuery.data?.items.length}
      />
    </Panel>
  );
}

export default function SearchPage() {
  const { runId, runQuery } = useRun();
  const [searchParams, setSearchParams] = useSearchParams();
  const q = searchParams.get("q") ?? "";
  const [draft, setDraft] = useState(q);

  useEffect(() => setDraft(q), [q]);

  const network: NetworkQuery = useMemo(() => classifyNetworkQuery(q), [q]);
  const isVrpLookup = network.kind === "ip" || network.kind === "prefix";

  // A combined query ("1.0.0.1 AS13335") carries its own ASN — prefill the
  // ?asn= filter so the VRP results apply it (the user can still override).
  useEffect(() => {
    if (isVrpLookup && network.asn !== undefined) {
      if (searchParams.get("asn") !== String(network.asn)) {
        const params = new URLSearchParams(searchParams);
        params.set("asn", String(network.asn));
        setSearchParams(params, { replace: true, preventScrollReset: true });
      }
    }
  }, [isVrpLookup, network, searchParams, setSearchParams]);

  const searchQuery = useQuery({
    queryKey: ["search", runId, q],
    queryFn: () => searchRun(runId, q, 10),
    enabled: !isVrpLookup && q.trim().length > 0,
  });

  const onSubmit = (event: FormEvent) => {
    event.preventDefault();
    const params = new URLSearchParams(searchParams);
    if (draft.trim()) params.set("q", draft.trim());
    else params.delete("q");
    params.delete("asn");
    setSearchParams(params, { preventScrollReset: true });
  };

  if (runQuery.isError) {
    return (
      <div className="page">
        <PageHeader title="Search" />
        <ErrorBlock
          error={runQuery.error}
          onRetry={() => runQuery.refetch()}
          title={runErrorTitle(runId, runQuery.error)}
        />
      </div>
    );
  }

  const result = searchQuery.data;
  const totalHits =
    (result?.objects.length ?? 0) +
    (result?.repos.length ?? 0) +
    (result?.publicationPoints.length ?? 0);

  return (
    <div className="page">
      <PageHeader
        title="Search"
        subtitle="Object URI, SHA-256, repository host, publication point URI — or an IP / prefix for VRP lookup"
      />

      <form className="search-hero" role="search" onSubmit={onSubmit}>
        <input
          type="search"
          value={draft}
          onChange={(e) => setDraft(e.target.value)}
          placeholder="rsync://… · sha256… · 81.68.1.1 · 2001:db8::/32 · AS45090"
          aria-label="Search query"
          autoFocus
        />
        <button type="submit" className="btn primary">
          Search
        </button>
      </form>

      {network.kind === "asn" ? (
        <Notice kind="info">
          The VRP API cannot list VRPs by ASN alone — query an IP address or a
          CIDR prefix first, then narrow the hits with the ASN filter. The
          results below only cover objects, repositories and publication points.
        </Notice>
      ) : null}

      {!q ? (
        <EmptyBlock
          title="Enter a query"
          hint="Paste an object URI to jump straight to its detail page, an IP / prefix for a VRP lookup, or a hash / host to find matching entities."
        />
      ) : isVrpLookup ? (
        <VrpResults network={network} />
      ) : searchQuery.isError ? (
        <ErrorBlock error={searchQuery.error} onRetry={() => searchQuery.refetch()} title="Search failed" />
      ) : searchQuery.isPending ? (
        <EmptyBlock title="Searching…" />
      ) : !result || totalHits === 0 ? (
        <EmptyBlock title={`No results for “${q}”`} hint="URI search is exact; hash search accepts a prefix of 8+ hex characters." />
      ) : (
        <>
          {result.objects.length > 0 ? (
            <section className="result-group">
              <h3>Objects ({result.objects.length})</h3>
              {result.objects.map((obj) => (
                <Link
                  key={obj.objectInstanceId}
                  className="result-item"
                  to={withRunParam(`/objects/${encodeURIComponent(obj.objectInstanceId)}`, runId)}
                >
                  <span className="result-kind">
                    <FileBox size={14} aria-hidden="true" /> {objectTypeLabel(obj.objectType)}
                  </span>
                  <span className="result-main">
                    <span className="result-title">{obj.uri}</span>
                    <span className="result-sub mono">sha256 {obj.sha256 ?? "—"}</span>
                  </span>
                  <StatusPill status={obj.rejected ? "rejected" : obj.result} />
                </Link>
              ))}
            </section>
          ) : null}

          {result.repos.length > 0 ? (
            <section className="result-group">
              <h3>Repositories ({result.repos.length})</h3>
              {result.repos.map((repo) => (
                <Link
                  key={repo.repoId}
                  className="result-item"
                  to={withRunParam(`/repositories/${encodeURIComponent(repo.repoId)}`, runId)}
                >
                  <span className="result-kind">
                    <Server size={14} aria-hidden="true" /> repo
                  </span>
                  <span className="result-main">
                    <span className="result-title">{repo.host}</span>
                    <span className="result-sub mono">{repo.uri}</span>
                  </span>
                  <span className="chip">{repo.transport ?? "unknown"}</span>
                </Link>
              ))}
            </section>
          ) : null}

          {result.publicationPoints.length > 0 ? (
            <section className="result-group">
              <h3>Publication points ({result.publicationPoints.length})</h3>
              {result.publicationPoints.map((pp) => (
                <Link
                  key={pp.ppId}
                  className="result-item"
                  to={withRunParam(`/publication-points/${encodeURIComponent(pp.ppId)}`, runId)}
                >
                  <span className="result-kind">
                    <FolderTree size={14} aria-hidden="true" /> PP
                  </span>
                  <span className="result-main">
                    <span className="result-title">{pp.manifestRsyncUri ?? pp.ppId}</span>
                    <span className="result-sub mono">{pp.ppId}</span>
                  </span>
                  <StatusPill status={pp.repoTerminalState} />
                </Link>
              ))}
            </section>
          ) : null}
        </>
      )}

      <Panel title="Search syntax">
        <ul style={{ margin: 0, paddingLeft: 18, display: "flex", flexDirection: "column", gap: 6 }}>
          <li><code>rsync://</code> / <code>https://</code> URI — exact object match</li>
          <li>64 hex chars — object by SHA-256; 8+ hex chars — SHA-256 prefix scan</li>
          <li>IPv4 / IPv6 address or CIDR prefix — VRP lookup (covering VRPs), with optional ASN filter</li>
          <li>IP / prefix + ASN in one query (<code>1.0.0.1 AS13335</code>) — prefills the VRP ASN filter</li>
          <li><code>AS</code> number alone — not supported by the VRP API; combine with an IP or prefix</li>
          <li>any other text — substring match on repository host/URI and publication point URIs</li>
        </ul>
      </Panel>
    </div>
  );
}
