"use client";

import { useState, useMemo, useRef, useEffect, useCallback } from "react";
import { useProperties } from "./useProperties";
import DataTable from "./dataTable";
import PropertyFilters from "./PropertyFilters";
import { FilterGroups } from "@/app/utils/propertyFilters";
import {
  FunnelIcon,
  ChevronDownIcon,
  XMarkIcon,
  ArrowDownTrayIcon,
  ViewColumnsIcon,
} from "@heroicons/react/24/outline";
import { HomeIcon } from "@heroicons/react/24/outline";
import { sapToEpc } from "@/app/utils";
import { columns } from "@/app/portfolio/[slug]/components/propertyTableColumns";
import { PropertyWithRelations } from "@/app/db/schema/property";
import {
  TENURE_OPTIONS,
  MAINFUEL_OPTIONS,
  EnumOption,
} from "@/app/utils/propertyFilters";
import {
  OPTIONAL_COLUMN_IDS,
  OPTIONAL_COLUMN_LABELS,
} from "@/app/portfolio/[slug]/components/propertyTableColumns";
import {
  VisibilityState,
  Updater,
  PaginationState,
} from "@tanstack/react-table";
import { Tooltip } from "./Tooltip";

import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/app/shadcn_components/ui/dropdown-menu";

import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  DialogFooter,
} from "@/app/shadcn_components/ui/dialog";
import { Button } from "@/app/shadcn_components/ui/button";

/* ----------------------------------------
   Export helpers
----------------------------------------- */
const EXPORT_LIMIT = 1000;

function resolveEnumLabel(
  options: EnumOption[],
  dbValue: string | null | undefined,
): string {
  if (dbValue == null) return "";
  const opt = options.find((o) => o.dbValues.includes(dbValue));
  return opt?.label ?? dbValue;
}

function exportToCsv(data: PropertyWithRelations[]) {
  const headers = [
    "Address",
    "Postcode",
    "Property Ref",
    "Current EPC",
    "Lodged EPC",
    "Expected EPC",
    "EPC Expiry",
    "EPC Expired",
    "Plan Cost (£)",
    "Property Type",
    "Built Form",
    "Tenure",
    "Year Built",
    "Floor Area (m²)",
    "CO₂ Emissions (kg/m²/yr)",
    "Main Fuel",
  ];

  const rows = data.map((p) => {
    const lodgedLetter = p.originalSapPoints
      ? (sapToEpc(p.originalSapPoints) ?? "")
      : "";
    const expectedSap =
      (p.currentSapPoints ?? 0) + (p.totalRecommendationSapPoints ?? 0);
    const expectedLetter = expectedSap > 0 ? (sapToEpc(expectedSap) ?? "") : "";

    let expiryStr = "";
    if (p.epcLodgementDate) {
      const expiry = new Date(p.epcLodgementDate);
      expiry.setFullYear(expiry.getFullYear() + 10);
      expiryStr = expiry.toLocaleDateString("en-GB");
    }

    return [
      p.address ?? "",
      p.postcode ?? "",
      p.landlordPropertyId ?? "",
      p.currentEpcRating ?? "",
      lodgedLetter,
      expectedLetter,
      expiryStr,
      p.epcIsExpired ? "Yes" : "No",
      p.totalRecommendationCost ? p.totalRecommendationCost.toFixed(2) : "",
      p.propertyType ?? "",
      p.builtForm ?? "",
      resolveEnumLabel(TENURE_OPTIONS, p.tenure),
      p.yearBuilt ?? "",
      p.totalFloorArea != null ? p.totalFloorArea.toFixed(1) : "",
      p.co2Emissions != null ? p.co2Emissions.toFixed(1) : "",
      resolveEnumLabel(MAINFUEL_OPTIONS, p.mainfuel),
    ];
  });

  const csv = [headers, ...rows]
    .map((row) =>
      row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(","),
    )
    .join("\n");

  const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = "properties.csv";
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

/* ----------------------------------------
   Empty portfolio state
----------------------------------------- */
function EmptyPropertyState() {
  return (
    <div className="flex justify-center py-16">
      <div className="text-center text-gray-400">
        <HomeIcon className="h-16 w-16 mx-auto mb-4 text-gray-200" />
        <p>
          Hover over <strong>&ldquo;New Property&rdquo;</strong> to start adding properties
          to your portfolio.
        </p>
      </div>
    </div>
  );
}

/* ----------------------------------------
   Loading overlay
----------------------------------------- */
function LoadingOverlay() {
  return (
    <div className="absolute inset-0 z-10 rounded-xl bg-white/60 flex items-center justify-center pointer-events-none">
      <div className="flex flex-col items-center gap-2">
        <div className="h-6 w-6 animate-spin rounded-full border-2 border-gray-200 border-t-gray-700" />
        <span className="text-xs text-gray-500">Updating…</span>
      </div>
    </div>
  );
}

/* ----------------------------------------
   Quick filter dropdown button
----------------------------------------- */
type QuickFilterKey = "address" | "postcode" | "propertyRef";

interface QuickFilterDropdownProps {
  label: string;
  placeholder: string;
  committedValue: string;
  isOpen: boolean;
  onOpen: () => void;
  onCommit: (value: string) => void;
  onClear: () => void;
  inputWidth?: string;
}

function QuickFilterDropdown({
  label,
  placeholder,
  committedValue,
  isOpen,
  onOpen,
  onCommit,
  onClear,
  inputWidth = "w-52",
}: QuickFilterDropdownProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLInputElement>(null);
  const [draft, setDraft] = useState(committedValue);

  useEffect(() => {
    if (isOpen) {
      setDraft(committedValue);
      setTimeout(() => inputRef.current?.focus(), 0);
    }
  }, [isOpen, committedValue]);

  const commit = useCallback(() => {
    onCommit(draft.trim());
  }, [draft, onCommit]);

  useEffect(() => {
    if (!isOpen) return;
    function handleMouseDown(e: MouseEvent) {
      if (
        containerRef.current &&
        !containerRef.current.contains(e.target as Node)
      ) {
        commit();
      }
    }
    document.addEventListener("mousedown", handleMouseDown);
    return () => document.removeEventListener("mousedown", handleMouseDown);
  }, [isOpen, commit]);

  const isActive = Boolean(committedValue);

  return (
    <div ref={containerRef} className="relative">
      <button
        onClick={onOpen}
        className={[
          "flex items-center gap-1.5 h-8 px-3 rounded-lg border text-xs font-semibold transition shrink-0",
          isActive
            ? "border-brandblue bg-brandblue text-white"
            : "border-slate-200 bg-white text-primary hover:bg-slate-50",
        ].join(" ")}
      >
        <span>{label}</span>
        {isActive ? (
          <>
            <span className="opacity-75 max-w-[120px] truncate">
              : {committedValue}
            </span>
            <span
              role="button"
              tabIndex={0}
              onClick={(e) => {
                e.stopPropagation();
                onClear();
              }}
              onKeyDown={(e) => {
                if (e.key === "Enter") {
                  e.stopPropagation();
                  onClear();
                }
              }}
              className="ml-0.5 rounded-full hover:opacity-75"
            >
              <XMarkIcon className="h-3.5 w-3.5" />
            </span>
          </>
        ) : (
          <ChevronDownIcon className="h-3.5 w-3.5 opacity-50" />
        )}
      </button>

      {isOpen && (
        <div className="absolute left-0 top-full mt-1 z-30 bg-white border border-slate-200 rounded-lg shadow-md p-2 flex gap-1.5 items-center">
          <input
            ref={inputRef}
            className={`h-8 rounded-lg border border-slate-200 px-2.5 text-xs focus:outline-none focus:ring-2 focus:ring-brandblue/20 focus:border-brandblue ${inputWidth}`}
            placeholder={placeholder}
            value={draft}
            onChange={(e) => setDraft(e.target.value)}
            onKeyDown={(e) => {
              if (e.key === "Enter") commit();
              if (e.key === "Escape") {
                setDraft(committedValue);
                onCommit(committedValue);
              }
            }}
          />
          <button
            onClick={commit}
            className="h-8 px-2.5 rounded-lg bg-brandblue text-white text-xs font-semibold hover:opacity-90 transition whitespace-nowrap"
          >
            Apply
          </button>
        </div>
      )}
    </div>
  );
}

/* ----------------------------------------
   Main table
----------------------------------------- */
export default function PropertyTable({
  portfolioId,
}: {
  portfolioId: string;
}) {
  const [sidebarOpen, setSidebarOpen] = useState(false);

  const [committedAddress, setCommittedAddress] = useState("");
  const [committedPostcode, setCommittedPostcode] = useState("");
  const [committedPropertyRef, setCommittedPropertyRef] = useState("");
  const [openFilter, setOpenFilter] = useState<QuickFilterKey | null>(null);
  const [filterGroups, setFilterGroups] = useState<FilterGroups>([]);

  // Column visibility — lifted up from DataTable
  const [columnVisibility, setColumnVisibility] = useState<VisibilityState>(
    () => {
      const init: VisibilityState = {};
      OPTIONAL_COLUMN_IDS.forEach((id) => {
        init[id] = false;
      });
      return init;
    },
  );

  function commitFilter(field: QuickFilterKey, value: string) {
    if (field === "address") setCommittedAddress(value);
    if (field === "postcode") setCommittedPostcode(value);
    if (field === "propertyRef") setCommittedPropertyRef(value);
    setOpenFilter(null);
  }

  function clearAll() {
    setCommittedAddress("");
    setCommittedPostcode("");
    setCommittedPropertyRef("");
    setOpenFilter(null);
    setFilterGroups([]);
  }

  const allFilterGroups = useMemo((): FilterGroups => {
    const quick: FilterGroups = [];
    if (committedAddress)
      quick.push({
        id: "qa",
        conditions: [
          {
            id: "qa-c",
            field: "address",
            operator: "contains",
            value: committedAddress,
          },
        ],
      });
    if (committedPostcode)
      quick.push({
        id: "qp",
        conditions: [
          {
            id: "qp-c",
            field: "postcode",
            operator: "starts_with",
            value: committedPostcode,
          },
        ],
      });
    if (committedPropertyRef)
      quick.push({
        id: "qr",
        conditions: [
          {
            id: "qr-c",
            field: "propertyRef",
            operator: "contains",
            value: committedPropertyRef,
          },
        ],
      });
    return [...quick, ...filterGroups];
  }, [committedAddress, committedPostcode, committedPropertyRef, filterGroups]);

  const hasActiveFilters = allFilterGroups.length > 0;

  const {
    data: filteredResponse,
    isLoading,
    isFetching,
    isError,
  } = useProperties({
    portfolioId,
    filterGroups: allFilterGroups,
  });

  const queryData = useMemo(() => filteredResponse?.data ?? [], [filteredResponse?.data]);
  const filteredTotal = filteredResponse?.total ?? 0;

  // Second query for total (no filters) — React Query dedupes when filters are empty
  const { data: allResponse } = useProperties({
    portfolioId,
    filterGroups: [],
  });
  const totalCount = allResponse?.total ?? 0;

  const DISPLAY_LIMIT = 1000;
  const LOAD_MORE_SIZE = 100;

  // ── Extra rows (lazy-loaded pages beyond the initial 1 000) ──────────────
  // Keyed to the current filter set so they auto-clear on filter change.
  const filterKey = useMemo(
    () => JSON.stringify(allFilterGroups),
    [allFilterGroups],
  );
  const [extraState, setExtraState] = useState<{
    filterKey: string;
    rows: PropertyWithRelations[];
  }>({ filterKey: "", rows: [] });

  const extraRows = useMemo(
    () => (extraState.filterKey === filterKey ? extraState.rows : []),
    [extraState, filterKey],
  );

  // The full data visible to the table — initial batch + any lazy-loaded rows
  const tableData = useMemo(
    () => (extraRows.length > 0 ? [...queryData, ...extraRows] : queryData),
    [queryData, extraRows],
  );

  // Controlled pagination (lifted from DataTable so we can detect the last page)
  const [pagination, setPagination] = useState<PaginationState>({
    pageIndex: 0,
    pageSize: 7,
  });

  // Reset to page 1 whenever the filter changes
  const prevFilterKeyRef = useRef(filterKey);
  if (prevFilterKeyRef.current !== filterKey) {
    prevFilterKeyRef.current = filterKey;
    if (pagination.pageIndex !== 0)
      setPagination((p) => ({ ...p, pageIndex: 0 }));
  }

  const [isFetchingMore, setIsFetchingMore] = useState(false);

  const pageCount = Math.ceil(tableData.length / pagination.pageSize);
  const isOnLastPage = pageCount > 0 && pagination.pageIndex >= pageCount - 1;
  const hasMore = tableData.length < filteredTotal;

  const isAtDisplayLimit =
    queryData.length >= DISPLAY_LIMIT && filteredTotal > DISPLAY_LIMIT;

  const loadMore = useCallback(async () => {
    if (isFetchingMore || !hasMore || isLoading || isFetching) return;
    setIsFetchingMore(true);
    try {
      const res = await fetch("/api/properties", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          portfolioId,
          filters: allFilterGroups,
          offset: tableData.length,
          limit: LOAD_MORE_SIZE,
        }),
      });
      if (!res.ok) return;
      const json: { data: PropertyWithRelations[] } = await res.json();
      setExtraState((prev) => ({
        filterKey,
        rows:
          prev.filterKey === filterKey
            ? [...prev.rows, ...json.data]
            : json.data,
      }));
    } catch {
      // silently ignore — user can navigate away and back to retry
    } finally {
      setIsFetchingMore(false);
    }
  }, [
    isFetchingMore,
    hasMore,
    isLoading,
    isFetching,
    portfolioId,
    allFilterGroups,
    tableData.length,
    filterKey,
  ]);

  useEffect(() => {
    if (isOnLastPage && hasMore) loadMore();
  }, [isOnLastPage, hasMore, loadMore]);

  /* ----------------------------------------
     Delete preview state
  ----------------------------------------- */
  const [deletePropertyId, setDeletePropertyId] = useState<number | null>(null);
  const [deletePreview, setDeletePreview] = useState<
    { table: string; count: number }[] | null
  >(null);
  const [previewLoading] = useState(false);
  const [previewError] = useState<string | null>(null);

  return (
    <div className="py-4">
      {/* Action bar */}
      <div className="flex items-center justify-between mb-3">
        {/* Left: results count */}
        <div className="flex items-center gap-3">
          <span className="text-xs font-black uppercase tracking-wider text-primary">
            Results:
          </span>
          {isLoading ? (
            <span className="text-xs text-slate-400">Loading…</span>
          ) : (
            <span className="text-xs text-slate-500">
              Showing{" "}
              <span className="font-bold text-primary">
                {tableData.length.toLocaleString()}
              </span>{" "}
              of{" "}
              <span className="font-bold text-primary">
                {totalCount.toLocaleString()}
              </span>{" "}
              properties
            </span>
          )}
          {hasActiveFilters && (
            <button
              onClick={clearAll}
              className="text-xs text-slate-400 hover:text-primary underline"
            >
              Clear all
            </button>
          )}
        </div>

        {/* Right: action buttons */}
        <div className="flex items-center gap-2">
          {/* Filters toggle */}
          <button
            onClick={() => setSidebarOpen((o) => !o)}
            className={[
              "flex items-center gap-1.5 h-8 px-3 rounded-lg border text-xs font-semibold transition shrink-0",
              sidebarOpen
                ? "bg-primary text-white border-primary"
                : "border-slate-200 bg-white text-primary hover:bg-slate-50",
            ].join(" ")}
            title={sidebarOpen ? "Hide filters" : "Show filters"}
          >
            <FunnelIcon className="h-3.5 w-3.5" />
            Filters
          </button>

          {/* Edit Columns dropdown */}
          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <button className="flex items-center gap-1.5 h-8 px-3 rounded-lg border border-slate-200 bg-white text-xs font-semibold text-primary hover:bg-slate-50 transition">
                <ViewColumnsIcon className="h-3.5 w-3.5" />
                Edit Columns
              </button>
            </DropdownMenuTrigger>
            <DropdownMenuContent align="end" className="w-48">
              <DropdownMenuLabel className="text-xs">
                Optional Columns
              </DropdownMenuLabel>
              <DropdownMenuSeparator />
              {OPTIONAL_COLUMN_IDS.map((colId) => {
                const isVisible = columnVisibility[colId] !== false;
                return (
                  <DropdownMenuItem
                    key={colId}
                    className="flex items-center gap-2 cursor-pointer"
                    onSelect={(e) => {
                      e.preventDefault();
                      setColumnVisibility((prev) => ({
                        ...prev,
                        [colId]: !isVisible,
                      }));
                    }}
                  >
                    <input
                      type="checkbox"
                      checked={isVisible}
                      readOnly
                      className="h-3.5 w-3.5 accent-black"
                    />
                    <span className="text-xs">
                      {OPTIONAL_COLUMN_LABELS[colId]}
                    </span>
                  </DropdownMenuItem>
                );
              })}
            </DropdownMenuContent>
          </DropdownMenu>

          {/* Export */}
          {filteredTotal > EXPORT_LIMIT ? (
            <Tooltip content={`Export is limited to ${EXPORT_LIMIT.toLocaleString()} properties. Refine your filters to enable it.`}>
              <button
                disabled
                className="flex items-center gap-1.5 h-8 px-3 rounded-lg border border-slate-200 bg-slate-100 text-xs font-semibold text-slate-400 cursor-not-allowed opacity-60"
              >
                <ArrowDownTrayIcon className="h-3.5 w-3.5" />
                Export
                <span className="inline-flex items-center justify-center w-3.5 h-3.5 rounded-full bg-amber-400 text-white text-[9px] font-black leading-none">!</span>
              </button>
            </Tooltip>
          ) : (
            <button
              onClick={() => exportToCsv(tableData)}
              disabled={isLoading || tableData.length === 0}
              className="flex items-center gap-1.5 h-8 px-3 rounded-lg border border-slate-200 bg-slate-100 text-xs font-semibold text-primary hover:bg-slate-200 transition disabled:opacity-50 disabled:cursor-not-allowed"
            >
              <ArrowDownTrayIcon className="h-3.5 w-3.5" />
              Export
            </button>
          )}
        </div>
      </div>

      {/* Quick filters row */}
      <div className="flex items-center gap-2 mb-3 flex-wrap">
        <QuickFilterDropdown
          label="Address"
          placeholder="Contains…"
          committedValue={committedAddress}
          isOpen={openFilter === "address"}
          onOpen={() =>
            setOpenFilter(openFilter === "address" ? null : "address")
          }
          onCommit={(v) => commitFilter("address", v)}
          onClear={() => {
            setCommittedAddress("");
            setOpenFilter(null);
          }}
          inputWidth="w-52"
        />

        <QuickFilterDropdown
          label="Postcode"
          placeholder="e.g. E17"
          committedValue={committedPostcode}
          isOpen={openFilter === "postcode"}
          onOpen={() =>
            setOpenFilter(openFilter === "postcode" ? null : "postcode")
          }
          onCommit={(v) => commitFilter("postcode", v)}
          onClear={() => {
            setCommittedPostcode("");
            setOpenFilter(null);
          }}
          inputWidth="w-32"
        />

        <QuickFilterDropdown
          label="Property Ref"
          placeholder="Landlord ref…"
          committedValue={committedPropertyRef}
          isOpen={openFilter === "propertyRef"}
          onOpen={() =>
            setOpenFilter(openFilter === "propertyRef" ? null : "propertyRef")
          }
          onCommit={(v) => commitFilter("propertyRef", v)}
          onClear={() => {
            setCommittedPropertyRef("");
            setOpenFilter(null);
          }}
          inputWidth="w-40"
        />
      </div>

      {/* Display-limit notice */}
      {isAtDisplayLimit && hasMore && (
        <div className="mb-3 flex items-center gap-2 rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-xs text-slate-500">
          <span>
            Showing{" "}
            <span className="font-semibold text-primary">
              {tableData.length.toLocaleString()}
            </span>{" "}
            of{" "}
            <span className="font-semibold text-primary">
              {filteredTotal.toLocaleString()}
            </span>{" "}
            properties — more load automatically as you navigate to the last page.
          </span>
        </div>
      )}

      {/* Body: sidebar + table */}
      <div className="flex items-start">
        {/* Collapsible filter sidebar */}
        <div
          className={[
            "shrink-0 overflow-hidden transition-all duration-300 ease-in-out rounded-xl",
            sidebarOpen
              ? "w-72 opacity-100 border border-slate-100 bg-slate-50"
              : "w-0 opacity-0",
          ].join(" ")}
        >
          <div className="w-72">
            <p className="px-4 pt-4 pb-0 text-[9px] font-black text-primary uppercase tracking-widest">
              Curate Selection
            </p>
            <PropertyFilters
              filterGroups={filterGroups}
              onChange={setFilterGroups}
            />
          </div>
        </div>

        {/* Table area */}
        <div className="flex-1 min-w-0 bg-white rounded-xl border border-slate-100 shadow-sm relative">
          {((isFetching && !isLoading) || isFetchingMore) && <LoadingOverlay />}

          {isLoading ? (
            <div className="p-6 text-slate-400 text-sm">
              Loading properties…
            </div>
          ) : isError ? (
            <div className="p-6 text-red-500 text-sm">
              Failed to load properties.
            </div>
          ) : queryData.length === 0 && hasActiveFilters ? (
            <div className="p-10 text-center text-slate-500">
              <p className="text-sm">No properties match your filters.</p>
              <button
                onClick={clearAll}
                className="mt-3 text-xs text-primary underline"
              >
                Clear filters
              </button>
            </div>
          ) : queryData.length === 0 ? (
            <EmptyPropertyState />
          ) : (
            <DataTable
              data={tableData}
              columns={columns}
              onDeleteProperty={(id) => setDeletePropertyId(id)}
              columnVisibility={columnVisibility}
              onColumnVisibilityChange={
                setColumnVisibility as (
                  updater: Updater<VisibilityState>,
                ) => void
              }
              pagination={pagination}
              onPaginationChange={
                setPagination as (updater: Updater<PaginationState>) => void
              }
            />
          )}
        </div>
      </div>

      {/* Delete preview modal */}
      <Dialog
        open={deletePropertyId !== null}
        onOpenChange={(open) => {
          if (!open) {
            setDeletePropertyId(null);
            setDeletePreview(null);
          }
        }}
      >
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Delete property?</DialogTitle>
            <DialogDescription>
              {previewLoading
                ? "Calculating what will be deleted…"
                : "This action is permanent. Review the impact below."}
            </DialogDescription>
          </DialogHeader>

          {previewError && (
            <p className="text-sm text-red-600">{previewError}</p>
          )}

          {deletePreview && !previewLoading && (
            <ul className="space-y-1 text-sm">
              {deletePreview.map((row) => (
                <li key={row.table} className="flex justify-between">
                  <span className="capitalize">
                    {row.table.replace(/_/g, " ")}
                  </span>
                  <span className="font-medium">{row.count}</span>
                </li>
              ))}
            </ul>
          )}

          <DialogFooter>
            <Button
              variant="ghost"
              onClick={() => {
                setDeletePropertyId(null);
                setDeletePreview(null);
              }}
            >
              Cancel
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}
