"use client";

import { useCallback, useMemo, useState } from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import {
  ArrowDownToLine,
  ArrowUpDown,
  ChevronLeft,
  ChevronRight,
  Eye,
  Pencil,
  Plus,
  RefreshCw,
  Search,
  SlidersHorizontal,
  Trash2,
  X,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { PageHeader, StatusBadge } from "@/components/shared/page";
import { PreviewLink, usePreviewRole } from "@/components/layout/preview-context";
import { canManage } from "@/lib/preview";
import { downloadCsv } from "@/lib/export";
import { updateQuery } from "@/lib/list-query";
import {
  customerSearchParamsRecord,
  customerPageSizes,
  readCustomerListQuery,
  isCustomerSortAvailable,
  sortCustomerRows,
  type CustomerFilters,
  type CustomerListQuery,
  type CustomerSortKey,
} from "./customer-query";
import type { CustomerApiPage, CustomerApiUser } from "./customer-api-contract";
import { usePeople } from "./people-provider";
import { PersonForm } from "./person-form";
import { blankPerson } from "./fixtures";
import type { Person } from "./model";

const filterLabels: Record<keyof CustomerFilters, string> = {
  organizationCode: "Organization code",
  firstName: "First name",
  lastName: "Last name",
  email: "Email",
  phone: "Phone",
  active: "Status",
};
const filterQueryKeys: Record<keyof CustomerFilters, string> = {
  organizationCode: "organization_code",
  firstName: "firstname",
  lastName: "lastname",
  email: "email",
  phone: "phone",
  active: "active",
};
const emptyRows: CustomerApiUser[] = [];

function fullName(customer: CustomerApiUser) {
  return `${customer.firstName} ${customer.lastName}`.trim() || "—";
}

function exportRows(customers: readonly CustomerApiUser[]) {
  return customers.map((customer) => ({
    Name: fullName(customer),
    Email: customer.email,
    Phone: customer.phone,
    Status: customer.active ? "Active" : "Inactive",
    "Organization Code": customer.organizationCode,
    "Created At": customer.createdAt,
  }));
}

function personToCustomer(person: Person): CustomerApiUser {
  return {
    id: person.id,
    firstName: person.firstName,
    lastName: person.lastName,
    email: person.email,
    phone: person.phone,
    active: person.status === "Active",
    organizationCode: "TIAR-DEMO",
    createdAt: "2026-01-01T00:00:00.000Z",
  };
}

function createPreviewRows(
  people: readonly Person[],
  query: CustomerListQuery,
  emptyState: boolean,
): CustomerApiUser[] {
  const search = query.search.toLowerCase();
  const customers = emptyState
    ? []
    : people
        .filter((person) => person.role === "Customer")
        .map(personToCustomer)
        .filter((customer) => {
          const searchable = [
            customer.firstName,
            customer.lastName,
            customer.email,
            customer.phone,
            customer.organizationCode,
          ].join(" ").toLowerCase();
          return (
            (!search || searchable.includes(search)) &&
            (!query.filters.organizationCode || customer.organizationCode.toLowerCase().includes(query.filters.organizationCode.toLowerCase())) &&
            (!query.filters.firstName || customer.firstName.toLowerCase().includes(query.filters.firstName.toLowerCase())) &&
            (!query.filters.lastName || customer.lastName.toLowerCase().includes(query.filters.lastName.toLowerCase())) &&
            (!query.filters.email || customer.email.toLowerCase().includes(query.filters.email.toLowerCase())) &&
            (!query.filters.phone || customer.phone.includes(query.filters.phone)) &&
            (!query.filters.active || String(customer.active) === query.filters.active)
          );
        });
  return sortCustomerRows(customers, query.sortKey, query.sortOrder);
}

function createPreviewPage(
  people: readonly Person[],
  query: CustomerListQuery,
  emptyState: boolean,
): CustomerApiPage {
  const customers = createPreviewRows(people, query, emptyState);
  const totalDocs = customers.length;
  const totalPages = Math.ceil(totalDocs / query.limit);
  const page = Math.min(query.page, Math.max(1, totalPages));
  const pagingCounter = totalDocs ? (page - 1) * query.limit + 1 : 0;
  return {
    customers: customers.slice(pagingCounter ? pagingCounter - 1 : 0, page * query.limit),
    pagination: {
      totalDocs,
      limit: query.limit,
      totalPages,
      page,
      pagingCounter,
      hasPrevPage: page > 1,
      hasNextPage: page < totalPages,
      prevPage: page > 1 ? page - 1 : 0,
      nextPage: page < totalPages ? page + 1 : 0,
    },
  };
}

export function CustomerView({
  query: initialQuery,
  page,
  apiConfigured,
  error,
  previewEmpty = false,
}: {
  query: CustomerListQuery;
  page: CustomerApiPage | null;
  apiConfigured: boolean;
  error: string;
  previewEmpty?: boolean;
}) {
  const params = useSearchParams();
  const pathname = usePathname();
  const router = useRouter();
  const query = readCustomerListQuery(
    customerSearchParamsRecord(new URLSearchParams(params.toString())),
  );
  const { people, setPeople } = usePeople();
  const role = usePreviewRole();
  const previewActions = !apiConfigured && canManage(role);
  const [editing, setEditing] = useState<Person | null>(null);
  const [deleting, setDeleting] = useState<Person | null>(null);
  const [notice, setNotice] = useState("");
  const [filterDraft, setFilterDraft] = useState<CustomerFilters>(initialQuery.filters);
  const [filterOpen, setFilterOpen] = useState(false);
  const [downloadOpen, setDownloadOpen] = useState(false);
  const [downloadScope, setDownloadScope] = useState<"page" | "filtered">("page");
  const [downloadError, setDownloadError] = useState("");
  const [downloading, setDownloading] = useState(false);
  const previewPage = useMemo(
    () => createPreviewPage(people, query, previewEmpty || Boolean(error)),
    [people, query, previewEmpty, error],
  );
  const activePage = apiConfigured ? page : previewPage;
  const rows = activePage?.customers ?? emptyRows;
  const pagination = activePage?.pagination;
  const customers = rows;
  const selectedSortKey = isCustomerSortAvailable(query.sortKey, apiConfigured)
    ? query.sortKey
    : "firstName";
  const totalPages = Math.max(1, pagination?.totalPages ?? 0);
  const totalDocs = pagination?.totalDocs ?? 0;
  const currentPage = pagination?.page ?? query.page;
  const startIndex = totalDocs ? pagination?.pagingCounter ?? 0 : 0;
  const endIndex = totalDocs
    ? Math.min(startIndex + customers.length - 1, totalDocs)
    : 0;
  const activeFilters = (Object.entries(query.filters) as [keyof CustomerFilters, string][])
    .filter(([, value]) => value !== "");
  const firstPageNumber = Math.max(1, Math.min(currentPage - 2, totalPages - 4));
  const pageNumbers = Array.from(
    { length: Math.min(totalPages, 5) },
    (_, index) => firstPageNumber + index,
  );
  const updateListQuery = useCallback((updates: Record<string, string | null>) => {
    const next = updateQuery(params.toString(), updates);
    router.replace(next ? `${pathname}?${next}` : pathname, { scroll: false });
  }, [params, pathname, router]);

  function changeSort(key: CustomerSortKey) {
    if (!isCustomerSortAvailable(key, apiConfigured)) return;
    const sortOrder = query.sortKey === key && query.sortOrder === "asc" ? "desc" : "asc";
    updateListQuery({
      sort_by: key === "firstName" ? "firstname" : key,
      sort_order: sortOrder,
      paginate_page: "1",
    });
  }

  function removeFilter(key: keyof CustomerFilters) {
    updateListQuery({ [filterQueryKeys[key]]: null, paginate_page: "1" });
  }

  function resetFilters() {
    setFilterDraft({
      organizationCode: "",
      firstName: "",
      lastName: "",
      email: "",
      phone: "",
      active: "",
    });
    updateListQuery({
      organization_code: null,
      firstname: null,
      lastname: null,
      email: null,
      phone: null,
      active: null,
      paginate_page: "1",
    });
    setFilterOpen(false);
  }

  async function downloadCustomers() {
    if (downloadScope === "page") {
      downloadCsv("customers", exportRows(customers));
      setDownloadOpen(false);
      return;
    }
    if (!apiConfigured) {
      downloadCsv(
        "customers",
        exportRows(createPreviewRows(people, query, previewEmpty || Boolean(error))),
      );
      setDownloadOpen(false);
      return;
    }
    setDownloading(true);
    setDownloadError("");
    try {
      const exportParams = new URLSearchParams(params.toString());
      exportParams.delete("paginate_page");
      exportParams.delete("paginate_limit");
      const response = await fetch(`/api/customers/export?${exportParams.toString()}`, {
        cache: "no-store",
      });
      const result: unknown = await response.json().catch(() => null);
      if (
        !response.ok || !result || typeof result !== "object" ||
        !("customers" in result) || !Array.isArray(result.customers)
      ) {
        const message = result && typeof result === "object" &&
          "error" in result && typeof result.error === "string"
          ? result.error
          : "Unable to prepare this download. Please try again.";
        throw new Error(message);
      }
      downloadCsv("customers", exportRows(result.customers as CustomerApiUser[]));
      setDownloadOpen(false);
    } catch (cause) {
      setDownloadError(
        cause instanceof Error
          ? cause.message
          : "Unable to prepare this download. Please try again.",
      );
    } finally {
      setDownloading(false);
    }
  }

  return (
    <>
      <PageHeader title="Customers" />
      <div className="space-y-5">
        {notice && <p role="status" className="text-sm text-success">{notice}</p>}
        <section className="overflow-hidden rounded-xl border bg-card">
          <header className="border-b px-5 py-4 sm:px-6">
            <div className="flex flex-wrap items-center justify-between gap-3">
              <h2 className="font-semibold">Customer List</h2>
              {previewActions && (
                <Button type="button" size="sm" onClick={() => setEditing({ ...blankPerson, role: "Customer" })}>
                  <Plus aria-hidden="true" /> Add Customer
                </Button>
              )}
            </div>
          </header>
          <div className="space-y-5 p-4 sm:p-6">
            {error && (
              <div role="alert" className="rounded-lg border border-destructive/30 bg-destructive/10 px-4 py-3 text-sm text-destructive">
                <p>{error}</p>
                <Button variant="outline" size="sm" className="mt-3" onClick={() => router.refresh()}>
                  <RefreshCw className="size-4" /> Try again
                </Button>
              </div>
            )}
            {activeFilters.length > 0 && (
              <div className="flex flex-wrap items-center gap-2 rounded-lg border bg-muted/40 px-4 py-3">
                <span className="text-sm text-muted-foreground">
                  {activeFilters.length} filter{activeFilters.length === 1 ? "" : "s"} applied:
                </span>
                {activeFilters.map(([key, value]) => (
                  <span key={key} className="inline-flex items-center gap-1 rounded-full border bg-background px-2.5 py-1 text-xs">
                    {key === "active" ? `Status: ${value === "true" ? "Active" : "Inactive"}` : `${filterLabels[key]}: ${value}`}
                    <button type="button" aria-label={`Remove ${filterLabels[key]} filter`} onClick={() => removeFilter(key)}>
                      <X className="size-3" />
                    </button>
                  </span>
                ))}
                <button type="button" className="ml-1 text-sm font-medium text-destructive hover:underline" onClick={resetFilters}>
                  Clear all filters
                </button>
              </div>
            )}
            <div className="flex flex-col gap-3 border px-4 py-4 sm:flex-row sm:items-center sm:justify-between">
              <label className="flex items-center gap-3 text-sm text-muted-foreground">
                <span>Show</span>
                <select
                  aria-label="Show entries"
                  value={query.limit}
                  onChange={(event) => updateListQuery({ paginate_limit: event.target.value, paginate_page: "1" })}
                  className="h-9 rounded-lg border bg-background px-3 text-sm text-foreground"
                >
                  {customerPageSizes.map((size) => <option key={size} value={size}>{size}</option>)}
                </select>
                <span>entries</span>
              </label>
              <div className="flex flex-wrap items-center gap-2">
                <Button type="button" variant="outline" size="sm" onClick={() => {
                  setFilterDraft({ ...query.filters });
                  setFilterOpen(true);
                }}>
                  <SlidersHorizontal aria-hidden="true" /> Filter
                </Button>
                <Button type="button" variant="outline" size="sm" onClick={() => {
                  setDownloadError("");
                  setDownloadOpen(true);
                }}>
                  <ArrowDownToLine aria-hidden="true" /> Download
                </Button>
                <form className="relative min-w-0 w-full flex-1 basis-full sm:basis-auto sm:w-[250px] sm:flex-none" onSubmit={(event) => {
                  event.preventDefault();
                  const data = new FormData(event.currentTarget);
                  updateListQuery({ search: String(data.get("search") ?? "").trim() || null, paginate_page: "1" });
                }}>
                  <Search className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" aria-hidden="true" />
                  <input
                    key={query.search}
                    type="search"
                    name="search"
                    aria-label="Search customers"
                    defaultValue={query.search}
                    placeholder="Search customers"
                    className="h-9 w-full rounded-lg border bg-background pl-9 pr-3 text-sm outline-none focus:border-primary"
                  />
                  <button type="submit" className="sr-only">Search</button>
                </form>
              </div>
            </div>
            <div className="overflow-x-auto">
              <table className={`w-full ${previewActions ? "min-w-[850px]" : "min-w-[700px]"} border-collapse text-left`}>
                <thead>
                  <tr className="bg-muted/50 text-sm">
                    {([
                      ["firstName", "Name"],
                      ["email", "Email"],
                      ["phone", "Phone"],
                      ["active", "Status"],
                    ] as const).map(([key, label]) => (
                      <th
                        key={key}
                        aria-sort={selectedSortKey === key
                          ? (query.sortOrder === "asc" ? "ascending" : "descending")
                          : "none"}
                        className="border px-4 py-3 font-medium"
                      >
                        {!isCustomerSortAvailable(key, apiConfigured) ? (
                          <span className="whitespace-nowrap">{label}</span>
                        ) : (
                          <button
                            type="button"
                            onClick={() => changeSort(key)}
                            className="inline-flex items-center gap-2 whitespace-nowrap"
                          >
                            {label}<ArrowUpDown className="size-3" aria-hidden="true" />
                          </button>
                        )}
                      </th>
                    ))}
                    {previewActions && <th className="border px-4 py-3 font-medium">Actions</th>}
                  </tr>
                </thead>
                <tbody>
                  {customers.map((customer) => (
                    <tr key={customer.id} className="hover:bg-muted/30">
                      <td className="border px-4 py-4 text-sm font-medium">
                        <PreviewLink
                          href={`/customers/${encodeURIComponent(customer.id)}`}
                          className="text-foreground hover:text-success hover:underline"
                        >
                          {fullName(customer)}
                        </PreviewLink>
                      </td>
                      <td className="whitespace-nowrap border px-4 py-4 text-sm">{customer.email || "—"}</td>
                      <td className="whitespace-nowrap border px-4 py-4 text-sm">{customer.phone || "—"}</td>
                      <td className="border px-4 py-4 text-sm"><StatusBadge>{customer.active ? "Active" : "Inactive"}</StatusBadge></td>
                      {previewActions && (
                        <td className="border px-4 py-3">
                          <div className="flex items-center gap-1">
                            <PreviewLink href={`/customers/${customer.id}`} className="inline-flex size-9 items-center justify-center rounded-lg text-info hover:bg-muted" aria-label={`View ${fullName(customer)}`}>
                              <Eye className="size-4" />
                            </PreviewLink>
                            <Button variant="ghost" size="icon" aria-label={`Edit ${fullName(customer)}`} onClick={() => {
                              const person = people.find((item) => item.id === customer.id);
                              if (person) setEditing({ ...person });
                            }}>
                              <Pencil className="size-4" />
                            </Button>
                            <Button variant="ghost" size="icon" aria-label={`Delete ${fullName(customer)}`} onClick={() => {
                              const person = people.find((item) => item.id === customer.id);
                              if (person) setDeleting(person);
                            }}>
                              <Trash2 className="size-4" />
                            </Button>
                          </div>
                        </td>
                      )}
                    </tr>
                  ))}
                  {!customers.length && (
                    <tr>
                      <td colSpan={previewActions ? 5 : 4} className="h-40 border px-4 text-center text-sm text-muted-foreground">
                        {error
                          ? "Unable to display customers."
                          : query.search || activeFilters.length
                            ? "No customers match your search or filters."
                            : "No customers found."}
                      </td>
                    </tr>
                  )}
                </tbody>
              </table>
            </div>
            <div className="flex flex-col gap-4 border px-4 py-4 sm:flex-row sm:items-center sm:justify-between">
              <p className="text-center text-sm text-muted-foreground sm:text-left">
                Showing {startIndex} to {endIndex} of {totalDocs} entries
              </p>
              <nav aria-label="Pagination" className="flex flex-wrap items-center justify-center gap-1 sm:justify-end">
                <Button type="button" variant="outline" size="xs" disabled={currentPage <= 1} onClick={() => updateListQuery({ paginate_page: String(currentPage - 1) })}>
                  <ChevronLeft aria-hidden="true" /> Previous
                </Button>
                {pageNumbers.map((number) => (
                  <button
                    key={number}
                    type="button"
                    aria-label={`Go to page ${number}`}
                    aria-current={currentPage === number ? "page" : undefined}
                    className={`size-8 rounded-lg text-sm ${currentPage === number ? "bg-primary text-primary-foreground" : "hover:bg-muted"}`}
                    onClick={() => updateListQuery({ paginate_page: String(number) })}
                  >
                    {number}
                  </button>
                ))}
                <Button type="button" variant="outline" size="xs" disabled={!pagination?.hasNextPage} onClick={() => updateListQuery({ paginate_page: String(currentPage + 1) })}>
                  Next <ChevronRight aria-hidden="true" />
                </Button>
              </nav>
            </div>
          </div>
        </section>
      </div>

      <Dialog open={filterOpen} onOpenChange={setFilterOpen}>
        <DialogContent className="max-h-[90dvh] overflow-y-auto">
          <DialogHeader>
            <DialogTitle>Filter customers</DialogTitle>
            <DialogDescription>Choose customer fields to narrow the list.</DialogDescription>
          </DialogHeader>
          <form id="customer-filters" className="grid gap-4 sm:grid-cols-2" onSubmit={(event) => {
            event.preventDefault();
            const updates: Record<string, string | null> = { paginate_page: "1" };
            for (const key of Object.keys(filterQueryKeys) as (keyof CustomerFilters)[]) {
              updates[filterQueryKeys[key]] = filterDraft[key] || null;
            }
            updateListQuery(updates);
            setFilterOpen(false);
          }}>
            {(["organizationCode", "firstName", "lastName", "email", "phone"] as const).map((key) => (
              <label key={key} className="grid gap-1.5 text-sm">
                <span>{filterLabels[key]}</span>
                <input
                  value={filterDraft[key]}
                  onChange={(event) => setFilterDraft((current) => ({ ...current, [key]: event.target.value }))}
                  maxLength={100}
                  className="h-10 rounded-lg border bg-background px-3"
                />
              </label>
            ))}
            <label className="grid gap-1.5 text-sm">
              <span>Status</span>
              <select
                value={filterDraft.active}
                onChange={(event) => setFilterDraft((current) => ({ ...current, active: event.target.value as CustomerFilters["active"] }))}
                className="h-10 rounded-lg border bg-background px-3"
              >
                <option value="">Any status</option>
                <option value="true">Active</option>
                <option value="false">Inactive</option>
              </select>
            </label>
          </form>
          <DialogFooter>
            <Button type="button" variant="outline" onClick={resetFilters}>Clear all</Button>
            <Button type="submit" form="customer-filters">Apply filters</Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <Dialog open={downloadOpen} onOpenChange={(open) => {
        if (!downloading) setDownloadOpen(open);
      }}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Download customers</DialogTitle>
            <DialogDescription>Download the current page or all customer records matching these filters as a CSV file.</DialogDescription>
          </DialogHeader>
          <fieldset className="grid gap-3">
            <legend className="text-sm font-medium">Data to include</legend>
            <label className="flex items-center gap-2 text-sm">
              <input type="radio" name="download-scope" checked={downloadScope === "page"} onChange={() => setDownloadScope("page")} /> Current page ({customers.length})
            </label>
            <label className="flex items-center gap-2 text-sm">
              <input type="radio" name="download-scope" checked={downloadScope === "filtered"} onChange={() => setDownloadScope("filtered")} /> All filtered results ({totalDocs})
            </label>
          </fieldset>
          {downloadError && <p role="alert" className="text-sm text-destructive">{downloadError}</p>}
          <DialogFooter>
            <Button type="button" variant="outline" disabled={downloading} onClick={() => setDownloadOpen(false)}>Cancel</Button>
            <Button type="button" disabled={downloading || (downloadScope === "page" ? customers.length === 0 : totalDocs === 0)} onClick={() => void downloadCustomers()}>
              <ArrowDownToLine className="size-4" /> {downloading ? "Preparing…" : "Download CSV"}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      {previewActions && (
        <>
          <Dialog open={!!editing} onOpenChange={(open) => { if (!open) setEditing(null); }}>
            <DialogContent className="max-h-[90dvh] overflow-y-auto">
              <DialogHeader>
                <DialogTitle>{editing?.id ? "Edit Customer" : "Create Customer"}</DialogTitle>
                <DialogDescription>Save changes to the current browser session.</DialogDescription>
              </DialogHeader>
              {editing && (
                <PersonForm
                  key={editing.id || "new-customer"}
                  initial={editing}
                  people={people}
                  onCancel={() => setEditing(null)}
                  onSave={(person) => {
                    const next = {
                      ...person,
                      id: person.id || `customer-preview-${crypto.randomUUID()}`,
                    };
                    setPeople((all) => person.id
                      ? all.map((item) => item.id === person.id ? next : item)
                      : [next, ...all]);
                    setEditing(null);
                    setNotice("Customer saved in local preview.");
                  }}
                />
              )}
            </DialogContent>
          </Dialog>
          <Dialog open={!!deleting} onOpenChange={(open) => { if (!open) setDeleting(null); }}>
            <DialogContent>
              <DialogHeader>
                <DialogTitle>Delete {deleting?.name}?</DialogTitle>
                <DialogDescription>This removes only the local fixture record.</DialogDescription>
              </DialogHeader>
              <DialogFooter>
                <Button variant="outline" onClick={() => setDeleting(null)}>Cancel</Button>
                <Button variant="destructive" onClick={() => {
                  setPeople((all) => all.filter((person) => person.id !== deleting?.id));
                  setDeleting(null);
                  setNotice("Customer removed from local preview.");
                }}>Delete Preview</Button>
              </DialogFooter>
            </DialogContent>
          </Dialog>
        </>
      )}
    </>
  );
}
