"use client";

import { useState } from "react";
import { useMutation, useQuery } from "@tanstack/react-query";
import {
  PackageCheck,
  AlertTriangle,
  X,
  Download,
  Loader2,
  CheckCircle2,
} from "lucide-react";
import DocumentTable, { type DocumentSelection } from "./DocumentTable";
import type {
  ClassifiedDeal,
  DocStatusMap,
  PortfolioCapabilityType,
  ApprovalsByDeal,
} from "./types";
import {
  MAX_BULK_DOWNLOAD_PROPERTIES,
  buildSelectionConfig,
  bulkDownloadRefetchInterval,
  type BulkDownloadStatus,
} from "@/lib/bulkDocumentDownload/model";

interface Props {
  data: ClassifiedDeal[];
  onOpenDrawer: (
    dealId: string,
    uprn: string | null,
    landlordPropertyId: string | null,
    dealname: string | null,
    batch: string | null,
    batchDescription: string | null,
  ) => void;
  docStatusMap: DocStatusMap;
  portfolioId: string;
  userCapability: PortfolioCapabilityType;
  approvalsByDeal?: ApprovalsByDeal;
  /** Current Documents-tab scope — a real project code or the "__ALL__" sentinel. */
  currentProjectCode: string;
}

/** JSON body or null — never throws, so a 504's HTML page can't crash the UI. */
async function parseBody(res: Response): Promise<{ error?: string; taskId?: string } | null> {
  const text = await res.text().catch(() => "");
  if (!text) return null;
  try {
    return JSON.parse(text);
  } catch {
    return null;
  }
}

/**
 * Bulk document download on the Documents tab: a "Bulk download" button flips the
 * table into row-selection mode; the picked landlord property ids become a task
 * whose ZIP is emailed (and shown here while the page stays open). Scoped by the
 * tab's existing project/group selection.
 */
export default function DocumentBulkDownload({
  data,
  onOpenDrawer,
  docStatusMap,
  portfolioId,
  userCapability,
  approvalsByDeal,
  currentProjectCode,
}: Props) {
  const [selecting, setSelecting] = useState(false);
  const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
  const [done, setDone] = useState<{ taskId: string } | null>(null);

  const landlordPropertyIds = [...selectedIds];
  const built = buildSelectionConfig({
    scope: { kind: "project", projectCode: currentProjectCode },
    includeAll: false,
    landlordPropertyIds,
    portfolioId: Number(portfolioId),
  });

  const trigger = useMutation<{ taskId: string }, Error, void>({
    mutationFn: async () => {
      const res = await fetch(`/api/portfolio/${portfolioId}/bulk-document-download`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          scope: { kind: "project", projectCode: currentProjectCode },
          includeAll: false,
          landlordPropertyIds,
        }),
      });
      const body = await parseBody(res);
      if (!res.ok) {
        throw new Error(
          body?.error ??
            (res.status >= 500
              ? "The server didn’t respond in time. Your download may still be preparing — check your email shortly."
              : `Couldn’t start the download (${res.status}).`),
        );
      }
      if (!body?.taskId) throw new Error("Unexpected response from the server.");
      return { taskId: body.taskId };
    },
    onSuccess: ({ taskId }) => {
      setDone({ taskId });
      setSelecting(false);
      setSelectedIds(new Set());
    },
  });

  const status = useQuery<BulkDownloadStatus, Error>({
    queryKey: ["bulk-download-status", portfolioId, done?.taskId],
    enabled: !!done,
    refetchInterval: (d) => bulkDownloadRefetchInterval(d),
    queryFn: async () => {
      const res = await fetch(
        `/api/portfolio/${portfolioId}/bulk-document-download/${done!.taskId}`,
      );
      const body = await parseBody(res);
      if (!res.ok || !body) {
        throw new Error("Couldn’t check the download status.");
      }
      return body as unknown as BulkDownloadStatus;
    },
  });

  const selection: DocumentSelection | undefined = selecting
    ? {
        selectedIds,
        onToggle: (id) =>
          setSelectedIds((prev) => {
            const next = new Set(prev);
            if (next.has(id)) next.delete(id);
            else next.add(id);
            return next;
          }),
        onSetMany: (ids, on) =>
          setSelectedIds((prev) => {
            const next = new Set(prev);
            for (const id of ids) {
              if (on) next.add(id);
              else next.delete(id);
            }
            return next;
          }),
      }
    : undefined;

  const overCap = selectedIds.size > MAX_BULK_DOWNLOAD_PROPERTIES;

  return (
    <div className="space-y-3">
      {/* Delivery / status card */}
      {done && (
        <DeliveryCard
          status={status.data}
          isError={status.isError}
          onDismiss={() => setDone(null)}
        />
      )}

      {/* Bulk-download control bar */}
      {!selecting ? (
        <div className="flex items-center justify-between gap-3">
          <button
            onClick={() => setSelecting(true)}
            className="inline-flex items-center gap-2 h-9 px-3.5 rounded-lg border border-brandblue/20 bg-white text-sm font-medium text-brandblue hover:border-brandblue/40 hover:bg-brandlightblue/10 transition-colors"
          >
            <PackageCheck className="h-4 w-4" />
            Bulk download
          </button>
        </div>
      ) : (
        <div className="flex flex-wrap items-center gap-3 rounded-xl border border-brandblue/20 bg-brandlightblue/10 px-4 py-2.5">
          <span className="text-sm font-semibold text-brandblue">
            {selectedIds.size} selected
          </span>
          <span className="text-xs text-gray-500">
            Tick properties, or use the header box to select the page (then “select
            all across pages”).
          </span>
          {overCap && (
            <span className="text-xs font-medium text-red-600">
              Over the {MAX_BULK_DOWNLOAD_PROPERTIES} limit — narrow your selection.
            </span>
          )}
          {trigger.isError && (
            <span className="text-xs font-medium text-red-600">{trigger.error.message}</span>
          )}
          <div className="ml-auto flex items-center gap-2">
            <button
              onClick={() => {
                setSelecting(false);
                setSelectedIds(new Set());
                trigger.reset();
              }}
              className="inline-flex items-center gap-1.5 h-9 px-3 rounded-lg border border-gray-200 bg-white text-sm font-medium text-gray-600 hover:bg-gray-50 transition-colors"
            >
              <X className="h-3.5 w-3.5" />
              Cancel
            </button>
            <button
              onClick={() => trigger.mutate()}
              disabled={!built.ok || trigger.isLoading}
              className="inline-flex items-center gap-2 h-9 px-3.5 rounded-lg bg-brandblue text-sm font-semibold text-white hover:bg-brandblue/90 disabled:opacity-40 transition-colors"
            >
              {trigger.isLoading ? (
                <Loader2 className="h-4 w-4 animate-spin" />
              ) : (
                <Download className="h-4 w-4" />
              )}
              {trigger.isLoading ? "Starting…" : "Download selected"}
            </button>
          </div>
        </div>
      )}

      <DocumentTable
        data={data}
        onOpenDrawer={onOpenDrawer}
        docStatusMap={docStatusMap}
        portfolioId={portfolioId}
        userCapability={userCapability}
        approvalsByDeal={approvalsByDeal}
        selection={selection}
      />
    </div>
  );
}

function DeliveryCard({
  status,
  isError,
  onDismiss,
}: {
  status: BulkDownloadStatus | undefined;
  isError: boolean;
  onDismiss: () => void;
}) {
  const [showSkipped, setShowSkipped] = useState(false);
  const state = status?.state ?? "preparing";

  // Completed, but the link couldn't be surfaced in-app (unparseable outputs).
  // The worker still emails it — send the user there rather than hang.
  if (state === "ready" && !status?.delivery) {
    return (
      <div
        role="status"
        className="flex flex-wrap items-center gap-3 rounded-xl border border-emerald-200 bg-emerald-50/70 px-4 py-3"
      >
        <CheckCircle2 className="h-5 w-5 flex-none text-emerald-600" aria-hidden />
        <div className="min-w-0">
          <p className="text-sm font-semibold text-brandblue">Your download is ready</p>
          <p className="text-[13px] text-gray-600">
            We’ve emailed you the link — it’s valid for about 60 minutes.
          </p>
        </div>
        <button
          onClick={onDismiss}
          className="ml-auto inline-flex items-center gap-1.5 h-9 px-3 rounded-lg border border-gray-200 bg-white text-sm font-medium text-gray-600 hover:bg-gray-50 transition-colors"
        >
          Dismiss
        </button>
      </div>
    );
  }

  if (state === "ready" && status?.delivery) {
    const { presignedUrl, included, skipped } = status.delivery;
    return (
      <div
        role="status"
        className="rounded-xl border border-emerald-200 bg-emerald-50/70 px-4 py-3"
      >
        <div className="flex flex-wrap items-center gap-3">
          <CheckCircle2 className="h-5 w-5 flex-none text-emerald-600" aria-hidden />
          <div className="min-w-0">
            <p className="text-sm font-semibold text-brandblue">Your download is ready</p>
            <p className="text-[13px] text-gray-600">
              {included} {included === 1 ? "document" : "documents"} included
              {skipped.length > 0 && ` · ${skipped.length} skipped`}. We’ve also emailed
              the link — it’s valid for about 60 minutes.
            </p>
          </div>
          <a
            href={presignedUrl}
            className="ml-auto inline-flex items-center gap-2 h-9 px-3.5 rounded-lg bg-brandblue text-sm font-semibold text-white hover:bg-brandblue/90 transition-colors"
          >
            <Download className="h-4 w-4" />
            Download ZIP
          </a>
        </div>
        {skipped.length > 0 && (
          <div className="mt-2">
            <button
              onClick={() => setShowSkipped((s) => !s)}
              className="text-[12.5px] font-semibold text-brandmidblue hover:underline"
            >
              {showSkipped ? "Hide" : "Show"} {skipped.length} skipped
            </button>
            {showSkipped && (
              <ul className="mt-1.5 max-h-40 overflow-y-auto rounded-lg border border-gray-100 bg-white px-3 py-2 text-[12.5px] text-gray-600">
                {skipped.map((s, i) => (
                  <li key={`${s.landlord_property_id}-${i}`}>
                    <b className="font-semibold text-gray-700">{s.landlord_property_id}</b> — {s.reason}
                  </li>
                ))}
              </ul>
            )}
          </div>
        )}
      </div>
    );
  }

  if (state === "failed" || isError) {
    return (
      <div
        role="status"
        className="flex flex-wrap items-center gap-3 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3"
      >
        <AlertTriangle className="h-5 w-5 flex-none text-amber-500" aria-hidden />
        <div className="min-w-0">
          <p className="text-sm font-semibold text-brandblue">
            We couldn’t build this download
          </p>
          <p className="text-[13px] text-gray-600">
            The selection may contain no documents. Try a different selection.
          </p>
        </div>
        <button
          onClick={onDismiss}
          className="ml-auto inline-flex items-center gap-1.5 h-9 px-3 rounded-lg border border-gray-200 bg-white text-sm font-medium text-gray-600 hover:bg-gray-50 transition-colors"
        >
          Dismiss
        </button>
      </div>
    );
  }

  return (
    <div
      role="status"
      className="flex items-center gap-3 rounded-xl border border-brandblue/20 bg-brandlightblue/10 px-4 py-3"
    >
      <Loader2 className="h-5 w-5 flex-none animate-spin text-brandmidblue" aria-hidden />
      <div>
        <p className="text-sm font-semibold text-brandblue">
          Your download is being prepared — we’ll email you a link
        </p>
        <p className="text-[13px] text-gray-500">
          You can leave this page; the link will be in your inbox, and it’ll appear here
          if you stay.
        </p>
      </div>
    </div>
  );
}
