"use client";

import Link from "next/link";
import type { ChangeEvent, FormEvent, ReactNode } from "react";
import { useEffect, useMemo, useRef, useState } from "react";

import {
  ArrowUpRightIcon,
  Badge,
  ProgressBar,
} from "@/components/dashboard-ui";
import { PropertyMap } from "@/components/property-map";
import { ThemedDatePicker } from "@/components/themed-date-picker";
import type {
  ComplianceItem,
  MessageThread,
  Property,
  Resident,
  WorkOrder,
} from "@/lib/property-data-uk";
import {
  formatCompactCurrency,
  formatCurrency,
  formatDate,
  formatPercent,
  getPropertyCollectionRate,
  getPropertyOccupancy,
} from "@/lib/property-data-uk";
import { propertyCsvHeaders } from "@/lib/property-import";
import { buildLocalValuation, type PropertyValuation } from "@/lib/valuation";

type PropertyFlickerProps = {
  properties: Property[];
  residents: Resident[];
  workOrders: WorkOrder[];
  complianceItems: ComplianceItem[];
  messageThreads: MessageThread[];
};

type PropertyReminder = {
  id: string;
  label: string;
  dueDate: string;
  leadDays: number;
  category: "Bill" | "Compliance" | "Inspection";
};

type FlickerProperty = Property & {
  reminders?: PropertyReminder[];
  source?: "manual" | "csv";
};

type PropertyRecord = Record<string, string>;
type PortfolioFocusId =
  | "all"
  | "arrears"
  | "voids"
  | "compliance"
  | "repairs"
  | "epc"
  | "messages";

const ADDED_PROPERTIES_KEY = "propertypilot-added-properties-v1";
const defaultLeadDays = 14;
const portfolioFocusOptions: Array<{ id: PortfolioFocusId; label: string }> = [
  { id: "all", label: "All homes" },
  { id: "arrears", label: "Arrears" },
  { id: "voids", label: "Voids" },
  { id: "compliance", label: "Compliance risk" },
  { id: "repairs", label: "Open repairs" },
  { id: "epc", label: "EPC work" },
  { id: "messages", label: "Unread messages" },
];
const epcRatings: Array<Property["epcRating"]> = ["A", "B", "C", "D", "E", "F", "G"];
const councilTaxBands: Array<Property["councilTaxBand"]> = [
  "A",
  "B",
  "C",
  "D",
  "E",
  "F",
  "G",
  "H",
  "I",
];
const emissionZones: Array<Property["emissionZone"]> = ["None", "ULEZ", "CAZ", "LEZ"];
const floodRisks: Array<Property["floodRisk"]> = ["Low", "Medium", "High"];

function clampIndex(index: number, length: number) {
  if (index < 0) {
    return length - 1;
  }

  if (index >= length) {
    return 0;
  }

  return index;
}

function countOpenWorkOrders(workOrders: WorkOrder[]) {
  return workOrders.filter((order) => order.status !== "Resolved").length;
}

function countDueCompliance(items: ComplianceItem[]) {
  return items.filter((item) => item.status !== "Completed").length;
}

function todayIso() {
  return "2026-05-14";
}

function slugify(value: string) {
  const slug = value
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-+|-+$/g, "");

  return slug || `property-${Date.now()}`;
}

function readString(record: PropertyRecord, key: string, fallback = "") {
  return record[key]?.trim() || fallback;
}

function readNumber(record: PropertyRecord, key: string, fallback = 0) {
  const value = Number(readString(record, key));
  return Number.isFinite(value) ? value : fallback;
}

function readInteger(record: PropertyRecord, key: string, fallback = 0) {
  return Math.max(0, Math.round(readNumber(record, key, fallback)));
}

function readOption<T extends string>(
  record: PropertyRecord,
  key: string,
  options: readonly T[],
  fallback: T,
) {
  const value = readString(record, key);
  return options.includes(value as T) ? (value as T) : fallback;
}

function buildReminder(
  propertyId: string,
  id: string,
  label: string,
  dueDate: string,
  category: PropertyReminder["category"],
  leadDays: number,
) {
  if (!dueDate) {
    return null;
  }

  return {
    id: `${propertyId}-${id}`,
    label,
    dueDate,
    category,
    leadDays,
  };
}

function buildReminders(record: PropertyRecord, propertyId: string) {
  const leadDays = readInteger(record, "reminderLeadDays", defaultLeadDays);
  const reminders = [
    buildReminder(propertyId, "rent", "Rent collection due", readString(record, "rentDueDate"), "Bill", leadDays),
    buildReminder(propertyId, "council-tax", "Council tax due", readString(record, "councilTaxDueDate"), "Bill", leadDays),
    buildReminder(propertyId, "utilities", "Utilities due", readString(record, "utilitiesDueDate"), "Bill", leadDays),
    buildReminder(propertyId, "service-charge", "Service charge / ground rent due", readString(record, "serviceChargeDueDate"), "Bill", leadDays),
    buildReminder(propertyId, "insurance", "Buildings insurance renewal", readString(record, "insuranceRenewalDate"), "Bill", leadDays),
    buildReminder(propertyId, "mortgage", "Mortgage payment due", readString(record, "mortgageDueDate"), "Bill", leadDays),
    buildReminder(propertyId, "cp12", "Gas safety (CP12) due", readString(record, "cp12DueDate"), "Compliance", leadDays),
    buildReminder(propertyId, "eicr", "EICR due", readString(record, "eicrDueDate"), "Compliance", leadDays),
    buildReminder(propertyId, "fire-risk", "Fire risk assessment due", readString(record, "fireRiskAssessmentDueDate"), "Compliance", leadDays),
    buildReminder(propertyId, "licence", "Licence renewal due", readString(record, "licenceRenewalDate"), "Compliance", leadDays),
    buildReminder(propertyId, "deposit-audit", "Deposit protection audit", readString(record, "depositAuditDueDate"), "Compliance", leadDays),
    buildReminder(propertyId, "inspection", "Property inspection due", readString(record, "nextInspection"), "Inspection", leadDays),
  ];

  return reminders.filter((reminder): reminder is PropertyReminder => Boolean(reminder));
}

function propertyFromRecord(record: PropertyRecord, source: "manual" | "csv"): FlickerProperty {
  const name = readString(record, "name", "Untitled property");
  const slug = slugify(name);
  const id = `custom-${slug}-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
  const units = Math.max(1, readInteger(record, "units", 1));
  const occupiedUnits = Math.min(units, readInteger(record, "occupiedUnits", units));
  const monthlyRentRoll = readNumber(record, "monthlyRentRoll", 0);
  const arrearsAmount = readNumber(record, "arrearsAmount", 0);
  const monthlyCollected = readNumber(
    record,
    "monthlyCollected",
    Math.max(0, monthlyRentRoll - arrearsAmount),
  );

  return {
    id,
    slug,
    name,
    address: readString(record, "address", "Address not set"),
    city: readString(record, "city", "London"),
    postcode: readString(record, "postcode", "Postcode not set").toUpperCase(),
    region: readString(record, "region", "United Kingdom"),
    type: readString(record, "type", "Managed property"),
    manager: readString(record, "manager", "Unassigned"),
    units,
    occupiedUnits,
    monthlyRentRoll,
    monthlyCollected,
    arrearsAmount,
    residentScore: readNumber(record, "residentScore", 4),
    openTickets: 0,
    avgResponseHours: 0,
    nextInspection: readString(record, "nextInspection", todayIso()),
    parkingSpaces: readInteger(record, "parkingSpaces", 0),
    permitsIssued: readInteger(record, "permitsIssued", 0),
    evBays: readInteger(record, "evBays", 0),
    epcRating: readOption(record, "epcRating", epcRatings, "C"),
    epcExpires: readString(record, "epcExpires", "2029-01-01"),
    councilTaxBand: readOption(record, "councilTaxBand", councilTaxBands, "C"),
    localAuthority: readString(record, "localAuthority", "Local authority not set"),
    emissionZone: readOption(record, "emissionZone", emissionZones, "None"),
    transportScore: readNumber(record, "transportScore", 70),
    floodRisk: readOption(record, "floodRisk", floodRisks, "Low"),
    coordinates: {
      lat: readNumber(record, "latitude", 51.5072),
      lng: readNumber(record, "longitude", -0.1276),
    },
    amenities: readString(record, "amenities")
      .split(";")
      .map((item) => item.trim())
      .filter(Boolean),
    tagline: readString(
      record,
      "tagline",
      "Newly added property with reminders ready for bill, compliance, and inspection follow-up.",
    ),
    reminders: buildReminders(record, id),
    source,
  };
}

function recordFromForm(form: HTMLFormElement) {
  const formData = new FormData(form);
  const record: PropertyRecord = {};

  for (const [key, value] of formData.entries()) {
    record[key] = typeof value === "string" ? value : "";
  }

  return record;
}

function readAddedProperties() {
  try {
    const stored = window.localStorage.getItem(ADDED_PROPERTIES_KEY);
    const parsed = stored ? JSON.parse(stored) : [];

    return Array.isArray(parsed) ? (parsed as FlickerProperty[]) : [];
  } catch {
    return [];
  }
}

function parseCsv(text: string) {
  const rows: string[][] = [];
  let row: string[] = [];
  let value = "";
  let quoted = false;

  for (let index = 0; index < text.length; index += 1) {
    const char = text[index];
    const nextChar = text[index + 1];

    if (char === '"' && quoted && nextChar === '"') {
      value += '"';
      index += 1;
    } else if (char === '"') {
      quoted = !quoted;
    } else if (char === "," && !quoted) {
      row.push(value);
      value = "";
    } else if ((char === "\n" || char === "\r") && !quoted) {
      if (char === "\r" && nextChar === "\n") {
        index += 1;
      }

      row.push(value);
      if (row.some((cell) => cell.trim())) {
        rows.push(row);
      }
      row = [];
      value = "";
    } else {
      value += char;
    }
  }

  row.push(value);
  if (row.some((cell) => cell.trim())) {
    rows.push(row);
  }

  const [headers = [], ...dataRows] = rows;
  const normalisedHeaders = headers.map((header) => header.trim());

  if (!propertyCsvHeaders.some((header) => normalisedHeaders.includes(header))) {
    return [];
  }

  return dataRows.map((cells) => {
    const record: PropertyRecord = {};

    normalisedHeaders.forEach((header, index) => {
      record[header] = cells[index]?.trim() ?? "";
    });

    return record;
  });
}

function daysUntil(dueDate: string) {
  const today = new Date(todayIso());
  const target = new Date(dueDate);

  return Math.ceil((target.getTime() - today.getTime()) / (1000 * 60 * 60 * 24));
}

function reminderText(reminder: PropertyReminder) {
  const days = daysUntil(reminder.dueDate);

  if (days < 0) {
    return `${Math.abs(days)} days overdue`;
  }

  if (days === 0) {
    return "Due today";
  }

  return `Due in ${days} days`;
}

function reminderTone(reminder: PropertyReminder) {
  const days = daysUntil(reminder.dueDate);

  if (days <= 7) {
    return "red";
  }

  if (days <= reminder.leadDays) {
    return "amber";
  }

  return "neutral";
}

function remindersForProperty(
  property: FlickerProperty,
  propertyCompliance: ComplianceItem[],
) {
  const complianceReminders = propertyCompliance
    .filter((item) => item.status !== "Completed")
    .map<PropertyReminder>((item) => ({
      id: item.id,
      label: item.title,
      dueDate: item.dueDate,
      category: "Compliance",
      leadDays: defaultLeadDays,
    }));
  const baselineReminders: PropertyReminder[] = [
    {
      id: `${property.id}-inspection`,
      label: "Property inspection due",
      dueDate: property.nextInspection,
      category: "Inspection",
      leadDays: defaultLeadDays,
    },
    {
      id: `${property.id}-epc-expiry`,
      label: "EPC expires",
      dueDate: property.epcExpires,
      category: "Compliance",
      leadDays: 60,
    },
  ];

  return [...(property.reminders ?? baselineReminders), ...complianceReminders].sort(
    (a, b) => a.dueDate.localeCompare(b.dueDate),
  );
}

function epcScore(rating: Property["epcRating"]) {
  const score = { A: 7, B: 6, C: 5, D: 4, E: 3, F: 2, G: 1 } as const;

  return score[rating];
}

function propertySearchText(property: FlickerProperty) {
  return [
    property.name,
    property.address,
    property.city,
    property.postcode,
    property.region,
    property.type,
    property.manager,
    property.localAuthority,
    property.tagline,
    property.amenities.join(" "),
  ]
    .join(" ")
    .toLowerCase();
}

function propertyMatchesFocus(
  property: FlickerProperty,
  focus: PortfolioFocusId,
  workOrders: WorkOrder[],
  complianceItems: ComplianceItem[],
  messageThreads: MessageThread[],
) {
  if (focus === "all") {
    return true;
  }

  if (focus === "arrears") {
    return property.arrearsAmount > 0;
  }

  if (focus === "voids") {
    return property.occupiedUnits < property.units;
  }

  if (focus === "compliance") {
    return complianceItems.some(
      (item) =>
        item.propertyId === property.id &&
        (item.status === "Blocked" || item.status === "Due soon"),
    );
  }

  if (focus === "repairs") {
    return workOrders.some(
      (order) => order.propertyId === property.id && order.status !== "Resolved",
    );
  }

  if (focus === "epc") {
    return epcScore(property.epcRating) < epcScore("C");
  }

  return messageThreads.some(
    (thread) => thread.propertyId === property.id && thread.unread > 0,
  );
}

function focusCount(
  properties: FlickerProperty[],
  focus: PortfolioFocusId,
  workOrders: WorkOrder[],
  complianceItems: ComplianceItem[],
  messageThreads: MessageThread[],
) {
  return properties.filter((property) =>
    propertyMatchesFocus(property, focus, workOrders, complianceItems, messageThreads),
  ).length;
}

export function PropertyFlicker({
  properties,
  residents,
  workOrders,
  complianceItems,
  messageThreads,
}: PropertyFlickerProps) {
  const [activeIndex, setActiveIndex] = useState(0);
  const [addedProperties, setAddedProperties] = useState<FlickerProperty[]>([]);
  const [storageReady, setStorageReady] = useState(false);
  const [showAddForm, setShowAddForm] = useState(false);
  const [importMessage, setImportMessage] = useState<string | null>(null);
  const [portfolioFocus, setPortfolioFocus] = useState<PortfolioFocusId>("all");
  const [portfolioQuery, setPortfolioQuery] = useState("");
  const [valuation, setValuation] = useState<PropertyValuation | null>(null);
  const [valuationLoading, setValuationLoading] = useState(false);
  const touchStart = useRef<{ x: number; y: number } | null>(null);
  const allProperties: FlickerProperty[] = useMemo(
    () => [...properties, ...addedProperties],
    [addedProperties, properties],
  );
  const property = allProperties[Math.min(activeIndex, allProperties.length - 1)];
  const normalisedPortfolioQuery = portfolioQuery.trim().toLowerCase();
  const focusedProperties = useMemo(
    () =>
      allProperties.filter(
        (item) =>
          propertyMatchesFocus(
            item,
            portfolioFocus,
            workOrders,
            complianceItems,
            messageThreads,
          ) &&
          (!normalisedPortfolioQuery ||
            propertySearchText(item).includes(normalisedPortfolioQuery)),
      ),
    [
      allProperties,
      complianceItems,
      messageThreads,
      normalisedPortfolioQuery,
      portfolioFocus,
      workOrders,
    ],
  );
  const propertyResidents = residents.filter(
    (resident) => resident.propertyId === property.id,
  );
  const propertyWorkOrders = workOrders.filter(
    (order) => order.propertyId === property.id,
  );
  const propertyCompliance = complianceItems.filter(
    (item) => item.propertyId === property.id,
  );
  const propertyMessages = messageThreads.filter(
    (thread) => thread.propertyId === property.id,
  );
  const occupancy = getPropertyOccupancy(property);
  const collectionRate = getPropertyCollectionRate(property);
  const propertyReminders = remindersForProperty(property, propertyCompliance);

  useEffect(() => {
    const frame = window.requestAnimationFrame(() => {
      setAddedProperties(readAddedProperties());
      setStorageReady(true);
    });

    return () => window.cancelAnimationFrame(frame);
  }, []);

  useEffect(() => {
    if (storageReady) {
      window.localStorage.setItem(
        ADDED_PROPERTIES_KEY,
        JSON.stringify(addedProperties),
      );
    }
  }, [addedProperties, storageReady]);

  useEffect(() => {
    const controller = new AbortController();

    async function loadValuation() {
      setValuationLoading(true);

      try {
        const response = await fetch(`/api/zoopla/valuation/${property.slug}`, {
          signal: controller.signal,
        });

        if (!response.ok) {
          setValuation(buildLocalValuation(property));
          return;
        }

        setValuation((await response.json()) as PropertyValuation);
      } catch {
        if (!controller.signal.aborted) {
          setValuation(null);
        }
      } finally {
        if (!controller.signal.aborted) {
          setValuationLoading(false);
        }
      }
    }

    setValuation(null);
    loadValuation();

    return () => controller.abort();
  }, [property]);

  useEffect(() => {
    if (
      focusedProperties.length > 0 &&
      !focusedProperties.some((item) => item.id === property.id)
    ) {
      setActiveIndex(allProperties.findIndex((item) => item.id === focusedProperties[0].id));
    }
  }, [allProperties, focusedProperties, property.id]);

  function goToPrevious() {
    setActiveIndex((current) => clampIndex(current - 1, allProperties.length));
  }

  function goToNext() {
    setActiveIndex((current) => clampIndex(current + 1, allProperties.length));
  }

  function handleAddProperty(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();

    const newProperty = propertyFromRecord(recordFromForm(event.currentTarget), "manual");
    setAddedProperties((current) => [...current, newProperty]);
    setActiveIndex(allProperties.length);
    setShowAddForm(false);
    setImportMessage(`${newProperty.name} added with ${newProperty.reminders?.length ?? 0} reminders.`);
    event.currentTarget.reset();
  }

  async function handleCsvUpload(event: ChangeEvent<HTMLInputElement>) {
    const input = event.currentTarget;
    const file = input.files?.[0];

    if (!file) {
      return;
    }

    try {
      const rows = parseCsv(await file.text());
      const imported = rows
        .filter((record) => readString(record, "name"))
        .map((record) => propertyFromRecord(record, "csv"));

      if (imported.length === 0) {
        setImportMessage("No properties found in that CSV.");
        return;
      }

      setAddedProperties((current) => [...current, ...imported]);
      setActiveIndex(allProperties.length);
      setImportMessage(`${imported.length} properties imported from ${file.name}.`);
    } catch {
      setImportMessage("That CSV could not be read. Download the template and try again.");
    } finally {
      input.value = "";
    }
  }

  function clearAddedProperties() {
    setAddedProperties([]);
    setActiveIndex((current) => Math.min(current, properties.length - 1));
    setImportMessage("Added and imported properties cleared from this browser.");
  }

  return (
    <div className="space-y-6">
      <section className="card-shell rounded-[28px] p-5 md:p-6">
        <div className="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
          <div>
            <p className="font-mono text-xs uppercase tracking-[0.26em] text-primary">
              Portfolio onboarding
            </p>
            <h2 className="mt-2 text-xl font-semibold text-foreground">
              Add properties and reminder dates
            </h2>
            <p className="mt-2 max-w-3xl text-sm leading-6 text-muted">
              Add one property with the questionnaire, or bulk import with the CSV
              template. Added properties are saved in this browser for the prototype.
            </p>
          </div>
          <div className="flex flex-wrap gap-3">
            <button
              className="rounded-full border border-primary/20 bg-primary px-5 py-3 text-sm font-semibold text-white transition hover:bg-primary/90"
              onClick={() => setShowAddForm((current) => !current)}
              type="button"
            >
              {showAddForm ? "Close form" : "Add property"}
            </button>
            <a
              className="rounded-full border border-border bg-white/72 px-5 py-3 text-sm font-semibold text-foreground transition hover:border-primary/20"
              download
              href="/api/properties/template"
            >
              Download CSV template
            </a>
            <label className="cursor-pointer rounded-full border border-border bg-white/72 px-5 py-3 text-sm font-semibold text-foreground transition hover:border-primary/20">
              Upload CSV
              <input
                accept=".csv,text/csv"
                className="sr-only"
                onChange={handleCsvUpload}
                type="file"
              />
            </label>
            {addedProperties.length > 0 ? (
              <button
                className="rounded-full border border-border bg-white/72 px-5 py-3 text-sm font-semibold text-danger transition hover:border-danger/25"
                onClick={clearAddedProperties}
                type="button"
              >
                Clear added
              </button>
            ) : null}
          </div>
        </div>

        {importMessage ? (
          <p className="mt-4 rounded-[18px] border border-border bg-white/72 px-4 py-3 text-sm font-medium text-foreground">
            {importMessage}
          </p>
        ) : null}

        {showAddForm ? <AddPropertyForm onSubmit={handleAddProperty} /> : null}
      </section>

      <section className="card-shell rounded-[28px] p-5 md:p-6">
        <div className="flex flex-col gap-5 xl:flex-row xl:items-start xl:justify-between">
          <div>
            <p className="font-mono text-xs uppercase tracking-[0.26em] text-primary">
              Portfolio focus
            </p>
            <h2 className="mt-2 text-xl font-semibold text-foreground">
              Find the clients that need attention
            </h2>
            <p className="mt-2 max-w-3xl text-sm leading-6 text-muted">
              Filter the portfolio by arrears, voids, certificates, repairs, EPC
              work, or unread messages, then jump straight into the client record.
            </p>
          </div>

          <label className="w-full xl:max-w-xs">
            <span className="text-sm font-semibold text-foreground">Search portfolio</span>
            <input
              className={fieldClassName()}
              onChange={(event) => setPortfolioQuery(event.target.value)}
              placeholder="Name, city, postcode, manager"
              type="search"
              value={portfolioQuery}
            />
          </label>
        </div>

        <div className="mt-5 flex flex-wrap gap-2">
          {portfolioFocusOptions.map((option) => {
            const selected = option.id === portfolioFocus;

            return (
              <button
                className={[
                  "rounded-full border px-4 py-2 text-sm font-semibold transition",
                  selected
                    ? "border-primary/25 bg-primary text-white"
                    : "border-border bg-white/72 text-foreground hover:border-primary/25",
                ].join(" ")}
                key={option.id}
                onClick={() => setPortfolioFocus(option.id)}
                type="button"
              >
                {option.label}{" "}
                <span className={selected ? "text-white/76" : "text-muted"}>
                  {focusCount(
                    allProperties,
                    option.id,
                    workOrders,
                    complianceItems,
                    messageThreads,
                  )}
                </span>
              </button>
            );
          })}
        </div>

        <div className="mt-5 grid gap-3 md:grid-cols-2 xl:grid-cols-4">
          {focusedProperties.length > 0 ? (
            focusedProperties.map((item) => {
              const itemIndex = allProperties.findIndex(
                (candidate) => candidate.id === item.id,
              );
              const itemOpenOrders = workOrders.filter(
                (order) => order.propertyId === item.id && order.status !== "Resolved",
              ).length;
              const itemUnread = messageThreads
                .filter((thread) => thread.propertyId === item.id)
                .reduce((sum, thread) => sum + thread.unread, 0);

              return (
                <button
                  className={[
                    "rounded-[18px] border p-4 text-left transition",
                    item.id === property.id
                      ? "border-primary/40 bg-primary/8"
                      : "border-border bg-white/72 hover:border-primary/25",
                  ].join(" ")}
                  key={item.id}
                  onClick={() => setActiveIndex(itemIndex)}
                  type="button"
                >
                  <div className="flex items-start justify-between gap-3">
                    <div>
                      <p className="font-semibold text-foreground">{item.name}</p>
                      <p className="mt-1 text-sm text-muted">
                        {item.city} · {item.postcode}
                      </p>
                    </div>
                    {item.occupiedUnits < item.units ? (
                      <Badge tone="amber">Void</Badge>
                    ) : null}
                  </div>
                  <div className="mt-4 flex flex-wrap gap-2">
                    {item.arrearsAmount > 0 ? (
                      <Badge tone="amber">
                        {formatCompactCurrency(item.arrearsAmount)} arrears
                      </Badge>
                    ) : null}
                    {itemOpenOrders > 0 ? (
                      <Badge tone="red">{itemOpenOrders} repair</Badge>
                    ) : null}
                    {epcScore(item.epcRating) < epcScore("C") ? (
                      <Badge tone="red">EPC {item.epcRating}</Badge>
                    ) : null}
                    {itemUnread > 0 ? (
                      <Badge tone="teal">{itemUnread} unread</Badge>
                    ) : null}
                    {item.arrearsAmount === 0 &&
                    itemOpenOrders === 0 &&
                    itemUnread === 0 &&
                    epcScore(item.epcRating) >= epcScore("C") &&
                    item.occupiedUnits === item.units ? (
                      <Badge tone="success">Steady</Badge>
                    ) : null}
                  </div>
                </button>
              );
            })
          ) : (
            <p className="rounded-[18px] border border-border bg-white/72 p-4 text-sm text-muted md:col-span-2 xl:col-span-4">
              No portfolio clients match that focus yet.
            </p>
          )}
        </div>
      </section>

      <section
        className="card-shell overflow-hidden rounded-[30px]"
        onTouchEnd={(event) => {
          if (!touchStart.current) {
            return;
          }

          const touch = event.changedTouches[0];
          const deltaX = touch.clientX - touchStart.current.x;
          const deltaY = touch.clientY - touchStart.current.y;
          touchStart.current = null;

          if (Math.abs(deltaX) < 56 || Math.abs(deltaY) > Math.abs(deltaX)) {
            return;
          }

          if (deltaX < 0) {
            goToNext();
          } else {
            goToPrevious();
          }
        }}
        onTouchStart={(event) => {
          const touch = event.changedTouches[0];
          touchStart.current = { x: touch.clientX, y: touch.clientY };
        }}
      >
        <div className="grid lg:grid-cols-[0.9fr_1.1fr]">
          <div className="bg-surface-deep p-6 text-white md:p-8">
            <div className="flex flex-wrap items-center gap-2">
              <Badge tone="teal">{property.type}</Badge>
              <Badge tone="neutral">{property.postcode}</Badge>
              <span className="rounded-full border border-white/15 bg-white/10 px-3 py-1 font-mono text-[11px] uppercase tracking-[0.2em] text-white/70">
                {activeIndex + 1} of {allProperties.length}
              </span>
              {property.source ? <Badge tone="amber">{property.source}</Badge> : null}
            </div>

            <div className="mt-8 space-y-4">
              <h2 className="display-title text-4xl font-semibold text-white md:text-5xl">
                {property.name}
              </h2>
              <p className="max-w-xl text-sm leading-6 text-white/68">
                {property.address}, {property.city}, {property.postcode}
              </p>
              <p className="max-w-2xl text-base leading-7 text-white/78">
                {property.tagline}
              </p>
            </div>

            <div className="mt-8 flex flex-wrap gap-3">
              <button
                className="rounded-full border border-white/20 bg-white/10 px-5 py-3 text-sm font-semibold text-white transition hover:bg-white/18"
                onClick={goToPrevious}
                type="button"
              >
                Previous
              </button>
              <button
                className="rounded-full border border-white/20 bg-white px-5 py-3 text-sm font-semibold text-surface-deep transition hover:bg-white/88"
                onClick={goToNext}
                type="button"
              >
                Next property
              </button>
              <Link
                className="inline-flex items-center gap-2 rounded-full border border-white/20 bg-white/10 px-5 py-3 text-sm font-semibold text-white transition hover:bg-white/18"
                href={`/properties/${property.slug}`}
              >
                Full record <ArrowUpRightIcon className="h-4 w-4" />
              </Link>
            </div>

            <div className="mt-6 flex flex-wrap gap-2">
              {properties.map((item, index) => (
                <button
                  aria-label={`Show ${item.name}`}
                  className={[
                    "h-2.5 rounded-full transition",
                    index === activeIndex
                      ? "w-10 bg-white"
                      : "w-2.5 bg-white/35 hover:bg-white/55",
                  ].join(" ")}
                  key={item.id}
                  onClick={() => setActiveIndex(index)}
                  type="button"
                />
              ))}
              {addedProperties.map((item, index) => {
                const itemIndex = properties.length + index;

                return (
                  <button
                    aria-label={`Show ${item.name}`}
                    className={[
                      "h-2.5 rounded-full transition",
                      itemIndex === activeIndex
                        ? "w-10 bg-white"
                        : "w-2.5 bg-white/35 hover:bg-white/55",
                    ].join(" ")}
                    key={item.id}
                    onClick={() => setActiveIndex(itemIndex)}
                    type="button"
                  />
                );
              })}
            </div>
          </div>

          <div className="p-5 md:p-6">
            <PropertyMap
              lat={property.coordinates.lat}
              lng={property.coordinates.lng}
              title={property.name}
            />
          </div>
        </div>
      </section>

      <section className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
        <div className="card-shell rounded-[24px] p-5">
          <p className="text-sm font-medium text-muted">Occupancy</p>
          <p className="mt-2 text-3xl font-semibold text-foreground">
            {property.occupiedUnits}/{property.units}
          </p>
          <div className="mt-4">
            <ProgressBar value={occupancy} tone="teal" />
          </div>
          <p className="mt-3 text-sm text-muted">{formatPercent(occupancy)} occupied</p>
        </div>

        <div className="card-shell rounded-[24px] p-5">
          <p className="text-sm font-medium text-muted">Rent collection</p>
          <p className="mt-2 text-3xl font-semibold text-foreground">
            {formatCompactCurrency(property.monthlyCollected)}
          </p>
          <div className="mt-4">
            <ProgressBar value={collectionRate} tone="amber" />
          </div>
          <p className="mt-3 text-sm text-muted">
            {formatPercent(collectionRate)} collected this period
          </p>
        </div>

        <div className="card-shell rounded-[24px] p-5">
          <p className="text-sm font-medium text-muted">Repairs</p>
          <p className="mt-2 text-3xl font-semibold text-foreground">
            {countOpenWorkOrders(propertyWorkOrders)}
          </p>
          <p className="mt-3 text-sm text-muted">
            Average response {property.avgResponseHours.toFixed(1)} hours
          </p>
        </div>

        <div className="card-shell rounded-[24px] p-5">
          <p className="text-sm font-medium text-muted">Compliance</p>
          <p className="mt-2 text-3xl font-semibold text-foreground">
            EPC {property.epcRating}
          </p>
          <p className="mt-3 text-sm text-muted">
            {countDueCompliance(propertyCompliance)} open checks
          </p>
        </div>
      </section>

      <section className="grid gap-6 xl:grid-cols-[0.95fr_1.05fr]">
        <div className="card-shell rounded-[28px] p-5 md:p-6">
          <div className="flex items-start justify-between gap-3">
            <div>
              <p className="font-mono text-xs uppercase tracking-[0.26em] text-primary">
                Zoopla valuation
              </p>
              <h3 className="mt-2 text-xl font-semibold text-foreground">
                {valuationLoading || !valuation
                  ? "Loading valuation"
                  : formatCurrency(valuation.estimate)}
              </h3>
            </div>
            {valuation ? (
              <Badge tone={valuation.connected ? "success" : "amber"}>
                {valuation.source}
              </Badge>
            ) : null}
          </div>

          {valuation ? (
            <>
              <dl className="mt-5 grid gap-3 text-sm text-muted sm:grid-cols-3">
                <div className="rounded-[18px] border border-border bg-white/72 p-4">
                  <dt>Estimate range</dt>
                  <dd className="mt-1 font-semibold text-foreground">
                    {formatCompactCurrency(valuation.lowEstimate)} -{" "}
                    {formatCompactCurrency(valuation.highEstimate)}
                  </dd>
                </div>
                <div className="rounded-[18px] border border-border bg-white/72 p-4">
                  <dt>Rental estimate</dt>
                  <dd className="mt-1 font-semibold text-foreground">
                    {formatCompactCurrency(valuation.rentalEstimate)} / mo
                  </dd>
                </div>
                <div className="rounded-[18px] border border-border bg-white/72 p-4">
                  <dt>Confidence</dt>
                  <dd className="mt-1 font-semibold text-foreground">
                    {valuation.confidence}
                  </dd>
                </div>
              </dl>
              <p className="mt-4 text-sm leading-6 text-muted">{valuation.note}</p>
            </>
          ) : (
            <p className="mt-5 text-sm text-muted">
              The valuation route is preparing the estimate for this property.
            </p>
          )}
        </div>

        <div className="card-shell rounded-[28px] p-5 md:p-6">
          <p className="font-mono text-xs uppercase tracking-[0.26em] text-primary">
            Local market context
          </p>
          <dl className="mt-5 grid gap-3 text-sm text-muted sm:grid-cols-2">
            <div className="rounded-[18px] border border-border bg-white/72 p-4">
              <dt>Local authority</dt>
              <dd className="mt-1 font-semibold text-foreground">
                {property.localAuthority}
              </dd>
            </div>
            <div className="rounded-[18px] border border-border bg-white/72 p-4">
              <dt>Council tax band</dt>
              <dd className="mt-1 font-semibold text-foreground">
                {property.councilTaxBand}
              </dd>
            </div>
            <div className="rounded-[18px] border border-border bg-white/72 p-4">
              <dt>Transport score</dt>
              <dd className="mt-1 font-semibold text-foreground">
                {property.transportScore}/100
              </dd>
            </div>
            <div className="rounded-[18px] border border-border bg-white/72 p-4">
              <dt>Flood risk</dt>
              <dd className="mt-1 font-semibold text-foreground">{property.floodRisk}</dd>
            </div>
          </dl>
        </div>
      </section>

      <section className="card-shell rounded-[28px] p-5 md:p-6">
        <div className="flex flex-col gap-2 md:flex-row md:items-end md:justify-between">
          <div>
            <p className="font-mono text-xs uppercase tracking-[0.26em] text-primary">
              Reminder schedule
            </p>
            <h3 className="mt-2 text-xl font-semibold text-foreground">
              Bills, inspections, and compliance dates
            </h3>
          </div>
          <Badge tone="neutral">{propertyReminders.length} tracked</Badge>
        </div>

        <div className="mt-5 grid gap-3 md:grid-cols-2 xl:grid-cols-3">
          {propertyReminders.map((reminder) => (
            <div
              className="rounded-[18px] border border-border bg-white/72 p-4"
              key={reminder.id}
            >
              <div className="flex items-start justify-between gap-3">
                <div>
                  <p className="font-semibold text-foreground">{reminder.label}</p>
                  <p className="mt-1 text-sm text-muted">
                    {formatDate(reminder.dueDate)} · {reminder.category}
                  </p>
                </div>
                <Badge tone={reminderTone(reminder)}>{reminderText(reminder)}</Badge>
              </div>
            </div>
          ))}
        </div>
      </section>

      <section className="grid gap-6 xl:grid-cols-3">
        <InfoList title="Tenants">
          {propertyResidents.map((resident) => (
            <div
              className="rounded-[18px] border border-border bg-white/72 p-4"
              key={resident.id}
            >
              <div className="flex items-start justify-between gap-3">
                <div>
                  <p className="font-semibold text-foreground">{resident.name}</p>
                  <p className="text-sm text-muted">
                    {resident.unit} · {resident.renewalStage}
                  </p>
                </div>
                <Badge tone={resident.balance > 0 ? "amber" : "teal"}>
                  {resident.balance > 0
                    ? formatCompactCurrency(resident.balance)
                    : "Clear"}
                </Badge>
              </div>
            </div>
          ))}
        </InfoList>

        <InfoList title="Repairs">
          {propertyWorkOrders.map((order) => (
            <div
              className="rounded-[18px] border border-border bg-white/72 p-4"
              key={order.id}
            >
              <div className="flex items-start justify-between gap-3">
                <div>
                  <p className="font-semibold text-foreground">{order.title}</p>
                  <p className="text-sm text-muted">
                    {order.unit} · {order.assignedTo}
                  </p>
                </div>
                <Badge tone={order.priority === "Critical" ? "red" : "amber"}>
                  {order.priority}
                </Badge>
              </div>
            </div>
          ))}
        </InfoList>

        <InfoList title="Compliance and messages">
          {propertyCompliance.slice(0, 3).map((item) => (
            <div
              className="rounded-[18px] border border-border bg-white/72 p-4"
              key={item.id}
            >
              <div className="flex items-start justify-between gap-3">
                <div>
                  <p className="font-semibold text-foreground">{item.title}</p>
                  <p className="text-sm text-muted">Due {formatDate(item.dueDate)}</p>
                </div>
                <Badge tone={item.status === "Blocked" ? "red" : "neutral"}>
                  {item.status}
                </Badge>
              </div>
            </div>
          ))}
          {propertyMessages.map((thread) => (
            <div
              className="rounded-[18px] border border-border bg-white/72 p-4"
              key={thread.id}
            >
              <p className="font-semibold text-foreground">{thread.title}</p>
              <p className="mt-1 text-sm text-muted">
                {thread.unread} unread · {thread.channel}
              </p>
            </div>
          ))}
        </InfoList>
      </section>
    </div>
  );
}

function AddPropertyForm({
  onSubmit,
}: {
  onSubmit: (event: FormEvent<HTMLFormElement>) => void;
}) {
  return (
    <form className="mt-6 space-y-6" onSubmit={onSubmit}>
      <FormSection title="Property details">
        <TextField label="Property name" name="name" required />
        <TextField label="Address" name="address" required />
        <TextField label="City" name="city" required />
        <TextField label="Postcode" name="postcode" required />
        <TextField label="Region" name="region" />
        <TextField label="Property type" name="type" placeholder="Build-to-rent" />
        <TextField label="Property manager" name="manager" />
        <TextField label="Local authority" name="localAuthority" />
      </FormSection>

      <FormSection title="Units, rent, and amenities">
        <NumberField label="Total units" min={1} name="units" required />
        <NumberField label="Occupied units" min={0} name="occupiedUnits" />
        <NumberField label="Monthly rent roll (£)" min={0} name="monthlyRentRoll" />
        <NumberField label="Monthly collected (£)" min={0} name="monthlyCollected" />
        <NumberField label="Arrears (£)" min={0} name="arrearsAmount" />
        <NumberField label="Tenant score" max={5} min={0} name="residentScore" step="0.1" />
        <NumberField label="Parking spaces" min={0} name="parkingSpaces" />
        <NumberField label="Permits issued" min={0} name="permitsIssued" />
        <NumberField label="EV bays" min={0} name="evBays" />
        <TextField
          label="Amenities"
          name="amenities"
          placeholder="Concierge; gym; cycle store"
        />
      </FormSection>

      <FormSection title="UK market and location">
        <SelectField label="EPC rating" name="epcRating" options={epcRatings} />
        <DateField label="EPC expiry" name="epcExpires" />
        <SelectField label="Council tax band" name="councilTaxBand" options={councilTaxBands} />
        <SelectField label="Emission zone" name="emissionZone" options={emissionZones} />
        <SelectField label="Flood risk" name="floodRisk" options={floodRisks} />
        <NumberField label="Transport score" max={100} min={0} name="transportScore" />
        <NumberField label="Latitude" name="latitude" step="0.00001" />
        <NumberField label="Longitude" name="longitude" step="0.00001" />
      </FormSection>

      <FormSection title="Reminder questions">
        <DateField label="Next inspection date" name="nextInspection" />
        <DateField label="Rent due date" name="rentDueDate" />
        <DateField label="Council tax due date" name="councilTaxDueDate" />
        <DateField label="Utilities due date" name="utilitiesDueDate" />
        <DateField label="Service charge / ground rent due date" name="serviceChargeDueDate" />
        <DateField label="Buildings insurance renewal date" name="insuranceRenewalDate" />
        <DateField label="Mortgage due date" name="mortgageDueDate" />
        <DateField label="Gas safety (CP12) due date" name="cp12DueDate" />
        <DateField label="EICR due date" name="eicrDueDate" />
        <DateField label="Fire risk assessment due date" name="fireRiskAssessmentDueDate" />
        <DateField label="Licence renewal date" name="licenceRenewalDate" />
        <DateField label="Deposit audit due date" name="depositAuditDueDate" />
        <NumberField
          defaultValue={defaultLeadDays}
          label="Remind me how many days before"
          min={1}
          name="reminderLeadDays"
        />
      </FormSection>

      <label className="block">
        <span className="text-sm font-semibold text-foreground">Notes for the property card</span>
        <textarea
          className="mt-2 min-h-24 w-full rounded-[18px] border border-border bg-white/78 px-4 py-3 text-sm text-foreground outline-none transition placeholder:text-muted/70 focus:border-primary/50 focus:ring-4 focus:ring-primary/10"
          name="tagline"
          placeholder="Add any operational context your team should see first."
        />
      </label>

      <div className="flex flex-wrap justify-end gap-3">
        <button
          className="rounded-full border border-primary/20 bg-primary px-5 py-3 text-sm font-semibold text-white transition hover:bg-primary/90"
          type="submit"
        >
          Save property
        </button>
      </div>
    </form>
  );
}

function FormSection({
  title,
  children,
}: {
  title: string;
  children: ReactNode;
}) {
  return (
    <section className="rounded-[22px] border border-border bg-white/58 p-4">
      <h3 className="text-sm font-semibold text-foreground">{title}</h3>
      <div className="mt-4 grid gap-4 md:grid-cols-2 xl:grid-cols-4">{children}</div>
    </section>
  );
}

function fieldClassName() {
  return "mt-2 w-full rounded-[18px] border border-border bg-white/78 px-4 py-3 text-sm text-foreground outline-none transition placeholder:text-muted/70 focus:border-primary/50 focus:ring-4 focus:ring-primary/10";
}

function TextField({
  label,
  name,
  placeholder,
  required,
}: {
  label: string;
  name: string;
  placeholder?: string;
  required?: boolean;
}) {
  return (
    <label className="block">
      <span className="text-sm font-semibold text-foreground">{label}</span>
      <input
        className={fieldClassName()}
        name={name}
        placeholder={placeholder}
        required={required}
        type="text"
      />
    </label>
  );
}

function NumberField({
  defaultValue,
  label,
  max,
  min,
  name,
  required,
  step,
}: {
  defaultValue?: number;
  label: string;
  max?: number;
  min?: number;
  name: string;
  required?: boolean;
  step?: string;
}) {
  return (
    <label className="block">
      <span className="text-sm font-semibold text-foreground">{label}</span>
      <input
        className={fieldClassName()}
        defaultValue={defaultValue}
        max={max}
        min={min}
        name={name}
        required={required}
        step={step ?? "1"}
        type="number"
      />
    </label>
  );
}

function DateField({ label, name }: { label: string; name: string }) {
  return (
    <ThemedDatePicker label={label} name={name} tone="light" />
  );
}

function SelectField<T extends string>({
  label,
  name,
  options,
}: {
  label: string;
  name: string;
  options: readonly T[];
}) {
  return (
    <label className="block">
      <span className="text-sm font-semibold text-foreground">{label}</span>
      <select className={fieldClassName()} name={name}>
        {options.map((option) => (
          <option key={option} value={option}>
            {option}
          </option>
        ))}
      </select>
    </label>
  );
}

function InfoList({
  title,
  children,
}: {
  title: string;
  children: ReactNode;
}) {
  return (
    <section className="card-shell rounded-[28px] p-5 md:p-6">
      <h3 className="text-lg font-semibold text-foreground">{title}</h3>
      <div className="mt-4 space-y-3">{children}</div>
    </section>
  );
}
