import { useMemo, useState, useEffect, type MutableRefObject } from 'react';
import { useTranslation } from 'react-i18next';
import { Property } from '../../types';
import {
  formatDuration,
  formatAge,
  formatNumber,
  formatTransactionDate,
  formatYearMonth,
} from '../../lib/format';
import { getNum } from '../../lib/property-fields';
import { buildEpcCertificateUrl } from '../../lib/external-search';
import { useRetainedScrollTop } from '../../hooks/useRetainedScrollTop';
import InfoPopup from '../ui/InfoPopup';
import { SearchInput } from '../ui/SearchInput';
import { EmptyState } from '../ui/EmptyState';
import { InfoIcon } from '../ui/icons';
import { IndeterminateProgressBar } from '../ui/IndeterminateProgressBar';
import { ts } from '../../i18n/server';

interface PropertiesPaneProps {
  properties: Property[];
  total: number;
  loading: boolean;
  hexagonId: string | null;
  statsUseFilters: boolean;
  onStatsUseFiltersChange: (useFilters: boolean) => void;
  filtersActive: boolean;
  onLoadMore: () => void;
  onNavigateToSource?: (slug: string) => void;
  scrollTopRef?: MutableRefObject<number>;
  scrollRestoreKey?: string | null;
  scrollSaveDisabled?: boolean;
}

export function PropertiesPane({
  properties,
  total,
  loading,
  hexagonId,
  statsUseFilters,
  onStatsUseFiltersChange,
  filtersActive,
  onLoadMore,
  onNavigateToSource,
  scrollTopRef,
  scrollRestoreKey,
  scrollSaveDisabled,
}: PropertiesPaneProps) {
  const { t } = useTranslation();
  const [search, setSearch] = useState('');
  const [showInfo, setShowInfo] = useState(false);
  const { scrollRef, onScroll } = useRetainedScrollTop<HTMLDivElement>({
    restoreKey: scrollRestoreKey ?? hexagonId,
    scrollTopRef,
    suspendSave: scrollSaveDisabled ?? (loading && properties.length === 0),
  });

  useEffect(() => {
    setSearch('');
  }, [hexagonId]);

  const filtered = useMemo(() => {
    const query = search.trim().toLowerCase();
    return query
      ? properties.filter((p) => {
          const addr = (p.address || '').toLowerCase();
          const pc = (p.postcode || '').toLowerCase();
          return addr.includes(query) || pc.includes(query);
        })
      : properties;
  }, [properties, search]);

  if (!hexagonId) {
    return (
      <EmptyState
        icon={<InfoIcon className="w-8 h-8 text-warm-300 dark:text-warm-600" />}
        title={t('common.noAreaSelected')}
        description={t('common.noAreaSelectedDesc')}
        centered
      />
    );
  }

  return (
    <div className="relative flex h-full flex-col">
      <IndeterminateProgressBar show={loading && properties.length > 0} />
      <div
        ref={scrollRef}
        onScroll={onScroll}
        className="flex-1 overflow-y-auto pb-[env(safe-area-inset-bottom)]"
      >
        {showInfo && (
          <InfoPopup
            title={t('propertyCard.propertyData')}
            onClose={() => setShowInfo(false)}
            sourceLink={
              onNavigateToSource
                ? {
                    label: t('common.viewDataSource'),
                    onClick: () => {
                      onNavigateToSource('epc');
                      setShowInfo(false);
                    },
                  }
                : undefined
            }
          >
            <p className="text-sm text-warm-700 dark:text-warm-300 mb-4 leading-relaxed">
              {t('propertyCard.propertyDataDesc')}
            </p>
          </InfoPopup>
        )}

        <div className="p-2 space-y-2">
          <SearchInput
            value={search}
            onChange={setSearch}
            placeholder={t('propertyCard.searchPlaceholder')}
            className="p-2"
          />
          {filtersActive && (
            <div className="grid grid-cols-2 rounded-md bg-warm-200 p-0.5 dark:bg-navy-800">
              <button
                type="button"
                aria-pressed={statsUseFilters}
                onClick={() => onStatsUseFiltersChange(true)}
                className={`min-w-0 rounded px-2 py-1 text-center text-xs font-medium leading-tight break-words ${
                  statsUseFilters
                    ? 'bg-white text-teal-700 shadow-sm dark:bg-navy-700 dark:text-teal-300'
                    : 'text-warm-600 hover:text-warm-900 dark:text-warm-400 dark:hover:text-warm-100'
                }`}
              >
                {t('areaPane.matchingFiltersOption')}
              </button>
              <button
                type="button"
                aria-pressed={!statsUseFilters}
                onClick={() => onStatsUseFiltersChange(false)}
                className={`min-w-0 rounded px-2 py-1 text-center text-xs font-medium leading-tight break-words ${
                  !statsUseFilters
                    ? 'bg-white text-teal-700 shadow-sm dark:bg-navy-700 dark:text-teal-300'
                    : 'text-warm-600 hover:text-warm-900 dark:text-warm-400 dark:hover:text-warm-100'
                }`}
              >
                {t('areaPane.allPropertiesOption')}
              </button>
            </div>
          )}
        </div>

        <div>
          {loading && properties.length === 0 ? (
            <PropertyLoadingSkeleton />
          ) : (
            <>
              {filtered.map((property) => (
                <PropertyCard
                  key={`${property.lat},${property.lon}|${property.postcode ?? ''}|${property.address ?? ''}`}
                  property={property}
                />
              ))}
              {properties.length < total && (
                <button
                  onClick={onLoadMore}
                  disabled={loading}
                  className="w-full p-4 text-teal-600 dark:text-teal-400 hover:bg-teal-50 dark:hover:bg-teal-900/30 disabled:opacity-50 transition-colors"
                >
                  {loading ? (
                    <span className="flex items-center justify-center gap-2">
                      <span className="inline-block w-4 h-4 border-2 border-teal-600 dark:border-teal-400 border-t-transparent rounded-full animate-spin" />
                      {t('common.loading')}
                    </span>
                  ) : (
                    `${t('common.loadMore')} (${t('common.remaining', { count: total - properties.length })})`
                  )}
                </button>
              )}
            </>
          )}
        </div>
      </div>
    </div>
  );
}

function PropertyLoadingSkeleton() {
  return (
    <div className="space-y-0">
      {Array.from({ length: 5 }).map((_, idx) => (
        <div key={idx} className="p-4 border-b border-warm-100 dark:border-navy-800 animate-pulse">
          <div className="h-5 w-3/4 bg-warm-200 dark:bg-warm-700 rounded mb-2" />
          <div className="h-4 w-24 bg-warm-200 dark:bg-warm-700 rounded mb-3" />
          <div className="h-6 w-32 bg-warm-200 dark:bg-warm-700 rounded mb-3" />
          <div className="grid grid-cols-2 gap-x-4 gap-y-2">
            {Array.from({ length: 6 }).map((_, i) => (
              <div key={i} className="h-4 bg-warm-200 dark:bg-warm-700 rounded" />
            ))}
          </div>
        </div>
      ))}
    </div>
  );
}

function PropertyCard({ property }: { property: Property }) {
  const { t } = useTranslation();
  const price = getNum(property, 'Last known price');
  const estimatedPrice = getNum(property, 'Estimated current price');
  const pricePerSqm = getNum(property, 'Price per sqm');
  const estPricePerSqm = getNum(property, 'Est. price per sqm');
  const floorArea = getNum(property, 'Total floor area (sqm)');
  const rooms = getNum(property, 'Number of bedrooms & living rooms');
  const age = getNum(property, 'Construction year');
  const transactionDate = getNum(property, 'Date of last transaction');

  return (
    <div className="p-4 border-b border-warm-100 dark:border-warm-800 hover:bg-warm-50 dark:hover:bg-warm-800">
      <div className="flex items-start justify-between gap-2">
        <div className="min-w-0">
          <div className="font-semibold dark:text-warm-100">
            {property.address || t('propertyCard.unknownAddress')}
          </div>
          <div className="text-sm text-warm-600 dark:text-warm-400 flex items-center gap-1.5">
            {property.postcode}
            {property.former_council_house === 'Yes' && (
              <span className="text-xs bg-teal-50 dark:bg-teal-900/30 text-teal-700 dark:text-teal-400 rounded-full px-1.5 py-0.5 font-medium leading-none">
                {t('propertyCard.exCouncilBadge')}
              </span>
            )}
            {property.listed_building === 'Yes' && (
              <span className="text-xs bg-amber-50 dark:bg-amber-900/30 text-amber-700 dark:text-amber-300 rounded-full px-1.5 py-0.5 font-medium leading-none">
                {t('propertyCard.listedBuildingBadge')}
              </span>
            )}
          </div>
        </div>
      </div>

      {property.property_sub_type && (
        <div className="text-sm text-warm-600 dark:text-warm-400 mt-1">
          {property.property_sub_type}
        </div>
      )}

      {price !== undefined && (
        <div className="mt-2 text-lg font-bold text-teal-700 dark:text-teal-400">
          £{formatNumber(price)}
          {transactionDate !== undefined && (
            <span className="text-sm font-normal text-warm-600 dark:text-warm-400">
              {' '}
              ({formatTransactionDate(transactionDate)})
            </span>
          )}
          {pricePerSqm !== undefined && (
            <span className="text-sm font-normal text-warm-600 dark:text-warm-400">
              {' '}
              £{formatNumber(pricePerSqm)}/m²
            </span>
          )}
        </div>
      )}
      {estimatedPrice !== undefined && (
        <div className="text-sm text-warm-600 dark:text-warm-400">
          {t('propertyCard.estValue')}{' '}
          <span className="font-semibold text-teal-700 dark:text-teal-400">
            £{formatNumber(estimatedPrice)}
          </span>
          {estPricePerSqm !== undefined && <span> (£{formatNumber(estPricePerSqm)}/m²)</span>}
        </div>
      )}

      <div className="mt-2 grid grid-cols-2 gap-x-4 gap-y-1 text-sm dark:text-warm-300">
        {property.property_type && (
          <div>
            <span className="text-warm-500 dark:text-warm-400">{t('propertyCard.type')}</span>{' '}
            {ts(property.property_type)}
          </div>
        )}
        {property.built_form && (
          <div>
            <span className="text-warm-500 dark:text-warm-400">{t('propertyCard.builtForm')}</span>{' '}
            {ts(property.built_form)}
          </div>
        )}
        {property.duration && (
          <div>
            <span className="text-warm-500 dark:text-warm-400">{t('propertyCard.tenure')}</span>{' '}
            {formatDuration(property.duration)}
          </div>
        )}
        {property.within_conservation_area && (
          <div>
            <span className="text-warm-500 dark:text-warm-400">
              {t('propertyCard.withinConservationArea')}
            </span>{' '}
            {ts(property.within_conservation_area)}
          </div>
        )}
        {floorArea !== undefined && (
          <div>
            <span className="text-warm-500 dark:text-warm-400">{t('propertyCard.floorArea')}</span>{' '}
            {formatNumber(floorArea)}m²
          </div>
        )}
        {rooms !== undefined && (
          <div>
            <span className="text-warm-500 dark:text-warm-400">{t('propertyCard.rooms')}</span>{' '}
            {formatNumber(rooms)}
          </div>
        )}
        {age !== undefined && (
          <div>
            <span className="text-warm-500 dark:text-warm-400">{t('propertyCard.built')}</span>{' '}
            {formatAge(age, property.is_construction_date_approximate)}
          </div>
        )}
        {property.current_energy_rating && (
          <div>
            <span className="text-warm-500 dark:text-warm-400">{t('propertyCard.epcRating')}</span>{' '}
            {ts(property.current_energy_rating)}
          </div>
        )}
        {property.potential_energy_rating && (
          <div>
            <span className="text-warm-500 dark:text-warm-400">
              {t('propertyCard.epcPotential')}
            </span>{' '}
            {ts(property.potential_energy_rating)}
          </div>
        )}
      </div>

      {property.postcode && property.current_energy_rating && (
        <a
          href={buildEpcCertificateUrl(property.postcode)}
          target="_blank"
          rel="noopener noreferrer"
          className="mt-2 inline-flex items-center gap-1 text-sm text-teal-600 dark:text-teal-400 hover:underline"
        >
          {t('propertyCard.viewEpcCertificate')}
          <svg
            aria-hidden
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            strokeWidth="2"
            strokeLinecap="round"
            strokeLinejoin="round"
            className="w-3.5 h-3.5"
          >
            <path d="M7 17 17 7M9 7h8v8" />
          </svg>
        </a>
      )}

      <PropertyTimeline property={property} />
    </div>
  );
}

type TimelineEvent =
  | { kind: 'sale'; year: number; month: number; price: number; isNew: boolean; sortKey: number }
  | { kind: 'reno'; year: number; event: string; sortKey: number }
  | { kind: 'tenure'; year: number; status: string; sortKey: number }
  | { kind: 'built'; year: number; approximate: boolean; sortKey: number };

export function buildTimelineEvents(property: Property): TimelineEvent[] {
  const events: TimelineEvent[] = [];

  // Skip the most recent sale: it's already shown in the card headline.
  // historical_prices is sorted oldest→newest by the pipeline.
  const sales = property.historical_prices ?? [];
  const olderSales = sales.length > 0 ? sales.slice(0, -1) : [];
  for (const sale of olderSales) {
    events.push({
      kind: 'sale',
      year: sale.year,
      month: sale.month,
      price: sale.price,
      isNew: sale.is_new,
      sortKey: sale.year + (Math.max(sale.month, 1) - 1) / 12,
    });
  }

  for (const reno of property.renovation_history ?? []) {
    events.push({
      kind: 'reno',
      year: reno.year,
      event: reno.event,
      // Mid-year so renos sort between Jan and Dec sales of the same year.
      sortKey: reno.year + 0.5,
    });
  }

  for (const tenure of property.tenure_history ?? []) {
    events.push({
      kind: 'tenure',
      year: tenure.year,
      status: tenure.status,
      // Mid-year, like renos: a tenure change is dated only to the EPC year.
      sortKey: tenure.year + 0.5,
    });
  }

  const builtYear = getNum(property, 'Construction year');
  const approximate = property.is_construction_date_approximate ?? true;
  if (builtYear !== undefined && Number.isFinite(builtYear) && builtYear > 0) {
    const builtYearRounded = Math.round(builtYear);
    // New-builds (exact date from price-paid) duplicate the first sale's year.
    // Suppress the "Built" marker in that case since the sale carries the info.
    const newBuildDuplicate = !approximate && sales.some((sale) => sale.year === builtYearRounded);
    if (!newBuildDuplicate) {
      events.push({
        kind: 'built',
        year: builtYearRounded,
        approximate,
        sortKey: builtYear,
      });
    }
  }

  events.sort((a, b) => b.sortKey - a.sortKey);
  return events;
}

function PropertyTimeline({ property }: { property: Property }) {
  const { t } = useTranslation();
  const events = useMemo(() => buildTimelineEvents(property), [property]);
  if (events.length === 0) return null;

  // Translate the pipeline's canonical tenure status. Literal keys keep the
  // typed `t` happy; an unexpected status falls back to its raw value (the same
  // contract the renovation labels use) rather than vanishing.
  const tenureLabel = (status: string): string => {
    switch (status) {
      case 'Owner-occupied':
        return t('propertyCard.tenureOwnerOccupied');
      case 'Rented (private)':
        return t('propertyCard.tenureRentedPrivate');
      case 'Rented (social)':
        return t('propertyCard.tenureRentedSocial');
      default:
        return status;
    }
  };

  return (
    <div className="mt-3">
      <div className="text-xs text-warm-500 dark:text-warm-400 mb-1.5">
        {t('propertyCard.historyTitle')}
      </div>
      <ol className="relative ml-1.5 border-l border-warm-200 dark:border-warm-700">
        {events.map((event, idx) => (
          <li key={idx} className="relative pl-3 pb-1.5 last:pb-0">
            <TimelineMarker kind={event.kind} />
            <div className="text-sm leading-tight">
              {event.kind === 'sale' && (
                <>
                  <span className="font-semibold text-teal-700 dark:text-teal-400">
                    £{formatNumber(event.price)}
                  </span>
                  {event.isNew && (
                    <span className="ml-1.5 inline-block rounded px-1 py-0.5 text-[10px] font-medium uppercase tracking-wide bg-teal-50 text-teal-700 dark:bg-teal-900/40 dark:text-teal-300">
                      {t('propertyCard.historyNewBuild')}
                    </span>
                  )}
                  <span className="ml-1.5 text-xs text-warm-500 dark:text-warm-400">
                    {formatYearMonth(event.year, event.month)}
                  </span>
                </>
              )}
              {event.kind === 'reno' && (
                <>
                  <span className="text-warm-700 dark:text-warm-200">{event.event}</span>
                  <span className="ml-1.5 text-xs text-warm-500 dark:text-warm-400">
                    {event.year}
                  </span>
                </>
              )}
              {event.kind === 'tenure' && (
                <>
                  <span className="text-warm-700 dark:text-warm-200">
                    {tenureLabel(event.status)}
                  </span>
                  <span className="ml-1.5 text-xs text-warm-500 dark:text-warm-400">
                    {event.year}
                  </span>
                </>
              )}
              {event.kind === 'built' && (
                <>
                  <span className="text-warm-700 dark:text-warm-200">
                    {t('propertyCard.historyBuilt')}
                  </span>
                  <span className="ml-1.5 text-xs text-warm-500 dark:text-warm-400">
                    {event.approximate ? `~${event.year}` : event.year}
                  </span>
                </>
              )}
            </div>
          </li>
        ))}
      </ol>
    </div>
  );
}

function TimelineMarker({ kind }: { kind: TimelineEvent['kind'] }) {
  const base = 'absolute -left-[5px] top-1 w-2 h-2 rounded-full ring-2';
  if (kind === 'sale') {
    return (
      <span
        aria-hidden
        className={`${base} bg-teal-500 dark:bg-teal-400 ring-warm-50 dark:ring-warm-900`}
      />
    );
  }
  if (kind === 'reno') {
    return (
      <span
        aria-hidden
        className={`${base} bg-warm-400 dark:bg-warm-500 ring-warm-50 dark:ring-warm-900`}
      />
    );
  }
  if (kind === 'tenure') {
    return (
      <span
        aria-hidden
        className={`${base} bg-indigo-400 dark:bg-indigo-400 ring-warm-50 dark:ring-warm-900`}
      />
    );
  }
  return (
    <span
      aria-hidden
      className={`${base} bg-warm-50 border border-warm-400 dark:bg-warm-900 dark:border-warm-500 ring-warm-50 dark:ring-warm-900`}
    />
  );
}
