"use client";

import { FormEvent, useEffect, useRef, useState } from "react";

import { Badge, Panel } from "@/components/dashboard-ui";
import { ThemedDatePicker } from "@/components/themed-date-picker";
import type { Property } from "@/lib/property-data-uk";

type DocumentCategory =
  | "Insurance policy"
  | "Warranty"
  | "Gas safety certificate (CP12)"
  | "Electrical certificate (EICR)"
  | "EPC certificate"
  | "Fire safety certificate"
  | "Tenancy agreement"
  | "Deposit protection"
  | "Licence"
  | "Invoice or receipt"
  | "Appliance manual"
  | "Other PDF";

type StoredPropertyDocument = {
  id: string;
  propertyId: string;
  title: string;
  category: DocumentCategory;
  fileName: string;
  fileType: string;
  fileSize: number;
  reviewDate: string;
  notes: string;
  addedAt: string;
  file: Blob;
};

type UploadForm = {
  propertyId: string;
  title: string;
  category: DocumentCategory;
  reviewDate: string;
  notes: string;
};

type PropertyDocumentVaultProps = {
  properties: Property[];
  initialPropertyId?: string;
  title?: string;
  description?: string;
  compact?: boolean;
};

type BadgeTone = "teal" | "amber" | "red" | "neutral" | "success" | "slate";

const DB_NAME = "propertypilot-document-vault";
const DB_VERSION = 1;
const STORE_NAME = "propertyDocuments";

const documentCategories: DocumentCategory[] = [
  "Insurance policy",
  "Warranty",
  "Gas safety certificate (CP12)",
  "Electrical certificate (EICR)",
  "EPC certificate",
  "Fire safety certificate",
  "Tenancy agreement",
  "Deposit protection",
  "Licence",
  "Invoice or receipt",
  "Appliance manual",
  "Other PDF",
];

function createInitialForm(propertyId: string): UploadForm {
  return {
    propertyId,
    title: "",
    category: "Insurance policy",
    reviewDate: "",
    notes: "",
  };
}

function openDocumentDatabase() {
  return new Promise<IDBDatabase>((resolve, reject) => {
    if (typeof window === "undefined" || !("indexedDB" in window)) {
      reject(new Error("PDF storage is not available in this browser."));
      return;
    }

    const request = window.indexedDB.open(DB_NAME, DB_VERSION);

    request.onupgradeneeded = () => {
      const database = request.result;

      if (!database.objectStoreNames.contains(STORE_NAME)) {
        const store = database.createObjectStore(STORE_NAME, { keyPath: "id" });
        store.createIndex("propertyId", "propertyId", { unique: false });
        store.createIndex("category", "category", { unique: false });
        store.createIndex("reviewDate", "reviewDate", { unique: false });
      }
    };

    request.onerror = () => reject(request.error ?? new Error("Could not open PDF storage."));
    request.onsuccess = () => resolve(request.result);
  });
}

async function readStoredDocuments() {
  const database = await openDocumentDatabase();

  return new Promise<StoredPropertyDocument[]>((resolve, reject) => {
    const transaction = database.transaction(STORE_NAME, "readonly");
    const request = transaction.objectStore(STORE_NAME).getAll();

    request.onerror = () =>
      reject(request.error ?? new Error("Could not read saved PDFs."));
    request.onsuccess = () => resolve(request.result as StoredPropertyDocument[]);
    transaction.oncomplete = () => database.close();
    transaction.onerror = () => {
      database.close();
      reject(transaction.error ?? new Error("Could not read saved PDFs."));
    };
  });
}

async function saveStoredDocument(documentRecord: StoredPropertyDocument) {
  const database = await openDocumentDatabase();

  return new Promise<void>((resolve, reject) => {
    const transaction = database.transaction(STORE_NAME, "readwrite");
    const request = transaction.objectStore(STORE_NAME).put(documentRecord);

    request.onerror = () =>
      reject(request.error ?? new Error("Could not save this PDF."));
    transaction.oncomplete = () => {
      database.close();
      resolve();
    };
    transaction.onerror = () => {
      database.close();
      reject(transaction.error ?? new Error("Could not save this PDF."));
    };
  });
}

async function deleteStoredDocument(documentId: string) {
  const database = await openDocumentDatabase();

  return new Promise<void>((resolve, reject) => {
    const transaction = database.transaction(STORE_NAME, "readwrite");
    const request = transaction.objectStore(STORE_NAME).delete(documentId);

    request.onerror = () =>
      reject(request.error ?? new Error("Could not remove this PDF."));
    transaction.oncomplete = () => {
      database.close();
      resolve();
    };
    transaction.onerror = () => {
      database.close();
      reject(transaction.error ?? new Error("Could not remove this PDF."));
    };
  });
}

function createDocumentId() {
  if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
    return crypto.randomUUID();
  }

  return `doc-${Date.now()}-${Math.random().toString(16).slice(2)}`;
}

function formatFileSize(bytes: number) {
  if (bytes < 1024) {
    return `${bytes} B`;
  }

  const kilobytes = bytes / 1024;

  if (kilobytes < 1024) {
    return `${kilobytes.toFixed(1)} KB`;
  }

  return `${(kilobytes / 1024).toFixed(1)} MB`;
}

function formatDisplayDate(date: string) {
  if (!date) {
    return "No review date";
  }

  return new Intl.DateTimeFormat("en-GB", {
    day: "numeric",
    month: "short",
    year: "numeric",
  }).format(new Date(`${date}T00:00:00`));
}

function getDateStatus(date: string): { label: string; tone: BadgeTone; hint: string } {
  if (!date) {
    return {
      label: "No date",
      tone: "neutral",
      hint: "Add a renewal, expiry, or review date when you upload it.",
    };
  }

  const today = new Date();
  today.setHours(0, 0, 0, 0);

  const dueDate = new Date(`${date}T00:00:00`);
  const days = Math.ceil((dueDate.getTime() - today.getTime()) / 86_400_000);

  if (days < 0) {
    return {
      label: "Expired",
      tone: "red",
      hint: `${Math.abs(days)} days overdue`,
    };
  }

  if (days <= 30) {
    return {
      label: "Due soon",
      tone: "amber",
      hint: `${days} days left`,
    };
  }

  if (days <= 90) {
    return {
      label: "Upcoming",
      tone: "teal",
      hint: `${days} days left`,
    };
  }

  return {
    label: "Stored",
    tone: "success",
    hint: `${days} days left`,
  };
}

function getCategoryTone(category: DocumentCategory): BadgeTone {
  if (category.includes("certificate") || category === "Licence") {
    return "teal";
  }

  if (category === "Insurance policy" || category === "Warranty") {
    return "amber";
  }

  if (category === "Tenancy agreement" || category === "Deposit protection") {
    return "success";
  }

  return "neutral";
}

function stripPdfExtension(fileName: string) {
  return fileName.replace(/\.pdf$/i, "").replace(/[-_]+/g, " ").trim();
}

export function PropertyDocumentVault({
  properties,
  initialPropertyId,
  title = "Property vault",
  description = "Save PDFs for insurance, warranties, safety certificates, tenancy files, invoices, and anything a landlord needs at short notice.",
  compact = false,
}: PropertyDocumentVaultProps) {
  const firstPropertyId = initialPropertyId ?? properties[0]?.id ?? "";
  const [selectedPropertyId, setSelectedPropertyId] = useState(firstPropertyId);
  const [documents, setDocuments] = useState<StoredPropertyDocument[]>([]);
  const [form, setForm] = useState<UploadForm>(() => createInitialForm(firstPropertyId));
  const [selectedFile, setSelectedFile] = useState<File | null>(null);
  const [search, setSearch] = useState("");
  const [statusMessage, setStatusMessage] = useState("");
  const [error, setError] = useState("");
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const fileInputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    let active = true;

    async function loadDocuments() {
      try {
        const storedDocuments = await readStoredDocuments();

        if (active) {
          setDocuments(storedDocuments);
          setError("");
        }
      } catch (storageError) {
        if (active) {
          setError(
            storageError instanceof Error
              ? storageError.message
              : "Could not load saved PDFs.",
          );
        }
      } finally {
        if (active) {
          setLoading(false);
        }
      }
    }

    loadDocuments();

    return () => {
      active = false;
    };
  }, []);

  useEffect(() => {
    if (!initialPropertyId) {
      return;
    }

    setSelectedPropertyId(initialPropertyId);
    setForm((currentForm) => ({
      ...currentForm,
      propertyId: initialPropertyId,
    }));
  }, [initialPropertyId]);

  const selectedProperty =
    properties.find((property) => property.id === selectedPropertyId) ?? properties[0];
  const propertyDocuments = documents
    .filter((documentRecord) => documentRecord.propertyId === selectedPropertyId)
    .filter((documentRecord) => {
      const term = search.trim().toLowerCase();

      if (!term) {
        return true;
      }

      return [
        documentRecord.title,
        documentRecord.category,
        documentRecord.fileName,
        documentRecord.notes,
      ]
        .join(" ")
        .toLowerCase()
        .includes(term);
    })
    .sort((a, b) => {
      const left = a.reviewDate || "9999-12-31";
      const right = b.reviewDate || "9999-12-31";
      return left.localeCompare(right);
    });
  const totalStorage = documents.reduce(
    (sum, documentRecord) => sum + documentRecord.fileSize,
    0,
  );
  const certificateCount = propertyDocuments.filter((documentRecord) =>
    documentRecord.category.includes("certificate"),
  ).length;
  const warrantyCount = propertyDocuments.filter(
    (documentRecord) =>
      documentRecord.category === "Warranty" ||
      documentRecord.category === "Appliance manual",
  ).length;
  const criticalCount = propertyDocuments.filter((documentRecord) => {
    const status = getDateStatus(documentRecord.reviewDate);
    return status.label === "Expired" || status.label === "Due soon";
  }).length;

  function updateForm<Key extends keyof UploadForm>(key: Key, value: UploadForm[Key]) {
    setForm((currentForm) => ({
      ...currentForm,
      [key]: value,
    }));
  }

  function handlePropertyChange(propertyId: string) {
    setSelectedPropertyId(propertyId);
    updateForm("propertyId", propertyId);
  }

  function handleFileChange(file: File | null) {
    setSelectedFile(file);
    setStatusMessage("");
    setError("");

    if (file && !form.title) {
      updateForm("title", stripPdfExtension(file.name));
    }
  }

  async function handleUpload(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    setStatusMessage("");
    setError("");

    if (!selectedFile) {
      setError("Choose a PDF before saving it.");
      return;
    }

    const looksLikePdf =
      selectedFile.type === "application/pdf" ||
      selectedFile.name.toLowerCase().endsWith(".pdf");

    if (!looksLikePdf) {
      setError("Only PDF files can be saved in the document vault.");
      return;
    }

    const propertyId = form.propertyId || selectedPropertyId;

    if (!propertyId) {
      setError("Choose a property before saving this PDF.");
      return;
    }

    setSaving(true);

    const nextDocument: StoredPropertyDocument = {
      id: createDocumentId(),
      propertyId,
      title: form.title.trim() || stripPdfExtension(selectedFile.name) || "Property PDF",
      category: form.category,
      fileName: selectedFile.name,
      fileType: selectedFile.type || "application/pdf",
      fileSize: selectedFile.size,
      reviewDate: form.reviewDate,
      notes: form.notes.trim(),
      addedAt: new Date().toISOString(),
      file: selectedFile,
    };

    try {
      await saveStoredDocument(nextDocument);
      setDocuments((currentDocuments) => [nextDocument, ...currentDocuments]);
      setSelectedPropertyId(propertyId);
      setForm(createInitialForm(propertyId));
      setSelectedFile(null);
      setStatusMessage(`${nextDocument.title} has been saved to the property vault.`);

      if (fileInputRef.current) {
        fileInputRef.current.value = "";
      }
    } catch (storageError) {
      setError(
        storageError instanceof Error
          ? storageError.message
          : "Could not save this PDF.",
      );
    } finally {
      setSaving(false);
    }
  }

  function openPdf(documentRecord: StoredPropertyDocument) {
    const fileUrl = URL.createObjectURL(documentRecord.file);
    window.open(fileUrl, "_blank", "noopener,noreferrer");
    window.setTimeout(() => URL.revokeObjectURL(fileUrl), 60_000);
  }

  function downloadPdf(documentRecord: StoredPropertyDocument) {
    const fileUrl = URL.createObjectURL(documentRecord.file);
    const anchor = window.document.createElement("a");
    anchor.href = fileUrl;
    anchor.download = documentRecord.fileName;
    anchor.click();
    window.setTimeout(() => URL.revokeObjectURL(fileUrl), 1000);
  }

  async function removePdf(documentRecord: StoredPropertyDocument) {
    setStatusMessage("");
    setError("");

    try {
      await deleteStoredDocument(documentRecord.id);
      setDocuments((currentDocuments) =>
        currentDocuments.filter((savedDocument) => savedDocument.id !== documentRecord.id),
      );
      setStatusMessage(`${documentRecord.title} has been removed.`);
    } catch (storageError) {
      setError(
        storageError instanceof Error
          ? storageError.message
          : "Could not remove this PDF.",
      );
    }
  }

  return (
    <Panel
      title={title}
      description={description}
      action={
        <div className="flex flex-wrap gap-2">
          <Badge tone="slate">{documents.length} PDFs</Badge>
          <Badge tone={criticalCount > 0 ? "amber" : "success"}>
            {criticalCount > 0 ? `${criticalCount} need review` : "Vault tidy"}
          </Badge>
        </div>
      }
    >
      <div className="grid gap-5 xl:grid-cols-[0.9fr_1.1fr]">
        <form
          onSubmit={handleUpload}
          className="rounded-[28px] border border-border bg-surface-strong p-5"
        >
          <div className="flex items-start justify-between gap-3">
            <div>
              <p className="font-mono text-xs uppercase tracking-[0.26em] text-primary">
                Add PDF
              </p>
              <h3 className="mt-2 text-xl font-semibold tracking-tight text-foreground">
                Attach the file once, find it fast later
              </h3>
            </div>
            <Badge tone="teal">PDF only</Badge>
          </div>

          <div className="mt-5 grid gap-4">
            {properties.length > 1 ? (
              <label className="space-y-2 text-sm font-semibold text-foreground">
                Property
                <select
                  value={form.propertyId}
                  onChange={(event) => updateForm("propertyId", event.currentTarget.value)}
                  className="w-full rounded-2xl border border-border bg-background px-4 py-3 text-sm text-foreground outline-none transition focus:border-primary"
                >
                  {properties.map((property) => (
                    <option key={property.id} value={property.id}>
                      {property.name} - {property.postcode}
                    </option>
                  ))}
                </select>
              </label>
            ) : null}

            <label className="space-y-2 text-sm font-semibold text-foreground">
              PDF file
              <input
                ref={fileInputRef}
                type="file"
                accept="application/pdf,.pdf"
                onChange={(event) =>
                  handleFileChange(event.currentTarget.files?.[0] ?? null)
                }
                className="w-full rounded-2xl border border-dashed border-border bg-background px-4 py-3 text-sm text-muted file:mr-3 file:rounded-full file:border-0 file:bg-primary file:px-4 file:py-2 file:text-sm file:font-semibold file:text-white"
              />
            </label>

            <div className="grid gap-4 md:grid-cols-2">
              <label className="space-y-2 text-sm font-semibold text-foreground">
                File name
                <input
                  value={form.title}
                  onChange={(event) => updateForm("title", event.currentTarget.value)}
                  placeholder="Aviva landlord insurance 2026"
                  className="w-full rounded-2xl border border-border bg-background px-4 py-3 text-sm text-foreground outline-none transition placeholder:text-muted/70 focus:border-primary"
                />
              </label>

              <label className="space-y-2 text-sm font-semibold text-foreground">
                Type
                <select
                  value={form.category}
                  onChange={(event) =>
                    updateForm("category", event.currentTarget.value as DocumentCategory)
                  }
                  className="w-full rounded-2xl border border-border bg-background px-4 py-3 text-sm text-foreground outline-none transition focus:border-primary"
                >
                  {documentCategories.map((category) => (
                    <option key={category} value={category}>
                      {category}
                    </option>
                  ))}
                </select>
              </label>
            </div>

            <ThemedDatePicker
              buttonClassName="rounded-2xl border-border bg-background px-4 py-3 text-sm"
              label="Renewal, expiry, or review date"
              labelClassName="text-sm font-semibold text-foreground"
              onChange={(nextDate) => updateForm("reviewDate", nextDate)}
              tone="light"
              value={form.reviewDate}
            />

            <label className="space-y-2 text-sm font-semibold text-foreground">
              Notes
              <textarea
                value={form.notes}
                onChange={(event) => updateForm("notes", event.currentTarget.value)}
                placeholder="Policy number, contractor, appliance covered, claim line, or anything you would otherwise hunt through emails for."
                rows={compact ? 3 : 4}
                className="w-full resize-none rounded-2xl border border-border bg-background px-4 py-3 text-sm text-foreground outline-none transition placeholder:text-muted/70 focus:border-primary"
              />
            </label>

            <button
              type="submit"
              disabled={saving}
              className="rounded-full bg-primary px-5 py-3 text-sm font-bold text-white transition hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-60"
            >
              {saving ? "Saving PDF..." : "Save PDF to property"}
            </button>
          </div>
        </form>

        <div className="space-y-5">
          <div className="grid gap-3 sm:grid-cols-3">
            <div className="rounded-[24px] border border-border bg-white/60 p-4">
              <p className="text-sm text-muted">Certificates</p>
              <p className="mt-2 text-2xl font-semibold text-foreground">
                {certificateCount}
              </p>
            </div>
            <div className="rounded-[24px] border border-border bg-white/60 p-4">
              <p className="text-sm text-muted">Warranties/manuals</p>
              <p className="mt-2 text-2xl font-semibold text-foreground">
                {warrantyCount}
              </p>
            </div>
            <div className="rounded-[24px] border border-border bg-white/60 p-4">
              <p className="text-sm text-muted">Stored locally</p>
              <p className="mt-2 text-2xl font-semibold text-foreground">
                {formatFileSize(totalStorage)}
              </p>
            </div>
          </div>

          <div className="rounded-[28px] border border-border bg-surface-strong p-5">
            <div className="flex flex-col gap-3 md:flex-row md:items-end md:justify-between">
              <div>
                <p className="font-mono text-xs uppercase tracking-[0.26em] text-primary">
                  Property vault
                </p>
                <h3 className="mt-2 text-xl font-semibold tracking-tight text-foreground">
                  {selectedProperty?.name ?? "Choose a property"}
                </h3>
                {selectedProperty ? (
                  <p className="mt-1 text-sm text-muted">
                    {selectedProperty.address}, {selectedProperty.city},{" "}
                    {selectedProperty.postcode}
                  </p>
                ) : null}
              </div>

              {properties.length > 1 ? (
                <select
                  value={selectedPropertyId}
                  onChange={(event) => handlePropertyChange(event.currentTarget.value)}
                  className="rounded-full border border-border bg-background px-4 py-2.5 text-sm font-semibold text-foreground outline-none transition focus:border-primary"
                  aria-label="Choose property vault"
                >
                  {properties.map((property) => (
                    <option key={property.id} value={property.id}>
                      {property.name}
                    </option>
                  ))}
                </select>
              ) : null}
            </div>

            <div className="mt-4">
              <label className="sr-only" htmlFor="document-search">
                Search the vault
              </label>
              <input
                id="document-search"
                value={search}
                onChange={(event) => setSearch(event.currentTarget.value)}
                placeholder="Search policies, warranties, certificates..."
                className="w-full rounded-2xl border border-border bg-background px-4 py-3 text-sm text-foreground outline-none transition placeholder:text-muted/70 focus:border-primary"
              />
            </div>

            {error ? (
              <p className="mt-4 rounded-2xl border border-danger/30 bg-danger-soft px-4 py-3 text-sm font-semibold text-danger">
                {error}
              </p>
            ) : null}
            {statusMessage ? (
              <p className="mt-4 rounded-2xl border border-success/30 bg-success/10 px-4 py-3 text-sm font-semibold text-success">
                {statusMessage}
              </p>
            ) : null}

            <div className="mt-5 space-y-3">
              {loading ? (
                <div className="rounded-[22px] border border-dashed border-border bg-background/55 p-5 text-sm text-muted">
                  Loading saved PDFs...
                </div>
              ) : null}

              {!loading && propertyDocuments.length === 0 ? (
                <div className="rounded-[22px] border border-dashed border-border bg-background/55 p-5">
                  <p className="font-semibold text-foreground">No PDFs saved yet.</p>
                  <p className="mt-2 text-sm leading-6 text-muted">
                    Start with landlord insurance, CP12 gas safety, EICR, EPC,
                    appliance warranties, tenancy agreement, deposit protection, and
                    key invoices. This turns the property page into the place you check
                    before calling anyone.
                  </p>
                </div>
              ) : null}

              {propertyDocuments.map((documentRecord) => {
                const dateStatus = getDateStatus(documentRecord.reviewDate);

                return (
                  <article
                    key={documentRecord.id}
                    className="rounded-[24px] border border-border bg-background/55 p-4"
                  >
                    <div className="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
                      <div className="min-w-0">
                        <div className="flex flex-wrap items-center gap-2">
                          <Badge tone={getCategoryTone(documentRecord.category)}>
                            {documentRecord.category}
                          </Badge>
                          <Badge tone={dateStatus.tone}>{dateStatus.label}</Badge>
                        </div>
                        <h4 className="mt-3 text-base font-semibold text-foreground">
                          {documentRecord.title}
                        </h4>
                        <p className="mt-1 text-sm text-muted">
                          {documentRecord.fileName} -{" "}
                          {formatFileSize(documentRecord.fileSize)}
                        </p>
                        <p className="mt-1 text-sm text-muted">
                          Review date: {formatDisplayDate(documentRecord.reviewDate)} -{" "}
                          {dateStatus.hint}
                        </p>
                        {documentRecord.notes ? (
                          <p className="mt-2 text-sm leading-6 text-muted">
                            {documentRecord.notes}
                          </p>
                        ) : null}
                      </div>

                      <div className="flex flex-wrap gap-2 md:justify-end">
                        <button
                          type="button"
                          onClick={() => openPdf(documentRecord)}
                          className="rounded-full border border-border bg-white/68 px-4 py-2 text-sm font-semibold text-foreground transition hover:border-primary/35 hover:text-primary"
                        >
                          Open
                        </button>
                        <button
                          type="button"
                          onClick={() => downloadPdf(documentRecord)}
                          className="rounded-full border border-border bg-white/68 px-4 py-2 text-sm font-semibold text-foreground transition hover:border-primary/35 hover:text-primary"
                        >
                          Download
                        </button>
                        <button
                          type="button"
                          onClick={() => removePdf(documentRecord)}
                          className="rounded-full border border-danger/30 bg-danger-soft px-4 py-2 text-sm font-semibold text-danger transition hover:bg-danger/15"
                        >
                          Remove
                        </button>
                      </div>
                    </div>
                  </article>
                );
              })}
            </div>
          </div>
        </div>
      </div>
    </Panel>
  );
}
