import type { FeatureFilters } from '../types';

export interface HexagonLocation {
  lat: number;
  lon: number;
  resolution: number;
  postcode?: string;
  isPostcode?: boolean;
}

const PROPERTY_TYPE_MAP: Record<
  string,
  { rightmove: string; onthemarket: string; zoopla: string }
> = {
  House: { rightmove: 'detached,semi-detached,terraced', onthemarket: 'property', zoopla: '' },
  Detached: { rightmove: 'detached', onthemarket: 'detached', zoopla: 'detached' },
  'Semi-Detached': {
    rightmove: 'semi-detached',
    onthemarket: 'semi-detached',
    zoopla: 'semi_detached',
  },
  Terraced: { rightmove: 'terraced', onthemarket: 'terraced', zoopla: 'terraced' },
  'Flats/Maisonettes': { rightmove: 'flat', onthemarket: 'flats', zoopla: 'flat' },
  Other: { rightmove: '', onthemarket: 'property', zoopla: '' },
};

export const H3_RADIUS_MILES: Record<number, number> = {
  4: 15,
  5: 6,
  6: 3,
  7: 1,
  8: 0.5,
  9: 1,
  10: 1,
  11: 1,
  12: 1,
};

export const POSTCODE_RADIUS_MILES = 0.25;

const RIGHTMOVE_RADII = [0.25, 0.5, 1, 3, 5, 10, 15, 20, 30, 40];
const RIGHTMOVE_OUTCODE_RADIUS_MILES = '0.0';
const OTM_RADII = [0.25, 0.5, 1, 3, 5, 10, 15, 20, 30, 40];
const ZOOPLA_RADII = [0.25, 0.5, 1, 3, 5, 10, 15, 20, 25, 30];

// Rightmove only accepts these specific price values
const RIGHTMOVE_PRICES = [
  50000, 60000, 70000, 80000, 90000, 100000, 110000, 120000, 125000, 130000, 140000, 150000, 160000,
  170000, 175000, 180000, 190000, 200000, 210000, 220000, 230000, 240000, 250000, 260000, 270000,
  280000, 290000, 300000, 325000, 350000, 375000, 400000, 425000, 450000, 475000, 500000, 550000,
  600000, 650000, 700000, 800000, 900000, 1000000, 1250000, 1500000, 1750000, 2000000, 2500000,
  3000000, 4000000, 5000000, 7500000, 10000000, 15000000, 20000000,
];

// OnTheMarket allowed buy prices
const OTM_PRICES = [
  50000, 60000, 70000, 80000, 90000, 100000, 110000, 120000, 125000, 130000, 140000, 150000, 160000,
  170000, 175000, 180000, 190000, 200000, 210000, 220000, 230000, 240000, 250000, 275000, 300000,
  325000, 350000, 375000, 400000, 425000, 450000, 475000, 500000, 550000, 600000, 650000, 700000,
  750000, 800000, 900000, 1000000, 1250000, 1500000, 2000000, 2500000, 3000000, 5000000, 7500000,
  10000000, 15000000,
];

// Zoopla allowed buy prices
const ZOOPLA_PRICES = [
  10000, 25000, 50000, 75000, 100000, 125000, 150000, 175000, 200000, 225000, 250000, 275000,
  300000, 325000, 350000, 375000, 400000, 425000, 450000, 475000, 500000, 550000, 600000, 650000,
  700000, 800000, 900000, 1000000, 1250000, 1500000, 1750000, 2000000, 2500000, 3000000, 4000000,
  5000000, 7500000, 10000000, 15000000,
];

function snapToAllowed(value: number, allowed: number[], direction: 'floor' | 'ceil'): number {
  if (direction === 'floor') {
    for (let i = allowed.length - 1; i >= 0; i--) {
      if (allowed[i] <= value) return allowed[i];
    }
    return allowed[0];
  }
  for (const p of allowed) {
    if (p >= value) return p;
  }
  return allowed[allowed.length - 1];
}

function nearestRadius(target: number, allowed: number[]): number {
  return allowed.reduce((best, r) => (Math.abs(r - target) < Math.abs(best - target) ? r : best));
}

interface SearchUrlOptions {
  location: HexagonLocation;
  filters: FeatureFilters;
  rightmoveLocationId?: string;
}

/** UK government "Find an energy certificate" domestic search, scoped to the
 * property's postcode. We don't store the per-certificate LMK key, so this lands
 * the user on the postcode result list where they pick the matching address. */
export function buildEpcCertificateUrl(postcode: string): string {
  const params = new URLSearchParams({ postcode });
  return `https://find-energy-certificate.service.gov.uk/find-a-certificate/search-by-postcode?${params.toString()}`;
}

export function buildRightmoveExactPostcodeRedirectUrl(
  postcode: string,
  targetUrl: string
): string {
  const params = new URLSearchParams();
  params.set('postcode', postcode);
  params.set('target', targetUrl);
  return `/api/rightmove-search?${params.toString()}`;
}

export function buildPropertySearchUrls({
  location,
  filters,
  rightmoveLocationId,
}: SearchUrlOptions): {
  rightmove: string | null;
  onthemarket: string;
  zoopla: string;
} | null {
  const { postcode, resolution, isPostcode } = location;
  if (!postcode) return null;

  const radiusMiles = isPostcode ? POSTCODE_RADIUS_MILES : (H3_RADIUS_MILES[resolution] ?? 1);

  const priceFilter = filters['Estimated current price'] ?? filters['Last known price'];
  const minPrice =
    Array.isArray(priceFilter) && typeof priceFilter[0] === 'number' ? priceFilter[0] : undefined;
  const maxPrice =
    Array.isArray(priceFilter) && typeof priceFilter[1] === 'number' ? priceFilter[1] : undefined;

  const propertyTypes = filters['Property type'];
  const selectedTypes =
    Array.isArray(propertyTypes) && typeof propertyTypes[0] === 'string'
      ? (propertyTypes as string[])
      : [];

  const tenureFilter = filters['Leasehold/Freehold'];
  const selectedTenures =
    Array.isArray(tenureFilter) && typeof tenureFilter[0] === 'string'
      ? (tenureFilter as string[])
      : [];

  const habitableRoomsFilter = filters['Number of bedrooms & living rooms'];
  const minBedrooms =
    Array.isArray(habitableRoomsFilter) && typeof habitableRoomsFilter[0] === 'number'
      ? Math.max(0, habitableRoomsFilter[0] - 1)
      : undefined;
  const maxBedrooms =
    Array.isArray(habitableRoomsFilter) && typeof habitableRoomsFilter[1] === 'number'
      ? Math.max(0, habitableRoomsFilter[1] - 1)
      : undefined;

  // Rightmove — requires locationIdentifier from typeahead API
  let rightmove: string | null = null;
  if (rightmoveLocationId) {
    const rmParams = new URLSearchParams();
    rmParams.set('searchLocation', postcode);
    rmParams.set('useLocationIdentifier', 'true');
    rmParams.set('locationIdentifier', rightmoveLocationId);
    rmParams.set(
      'radius',
      rightmoveLocationId.startsWith('OUTCODE^')
        ? RIGHTMOVE_OUTCODE_RADIUS_MILES
        : String(nearestRadius(radiusMiles, RIGHTMOVE_RADII))
    );
    if (minPrice !== undefined)
      rmParams.set('minPrice', String(snapToAllowed(minPrice, RIGHTMOVE_PRICES, 'floor')));
    if (maxPrice !== undefined)
      rmParams.set('maxPrice', String(snapToAllowed(maxPrice, RIGHTMOVE_PRICES, 'ceil')));
    if (minBedrooms !== undefined) rmParams.set('minBedrooms', String(minBedrooms));
    if (maxBedrooms !== undefined) rmParams.set('maxBedrooms', String(maxBedrooms));
    if (selectedTypes.length > 0) {
      const rmTypes = [
        ...new Set(
          selectedTypes.flatMap((t) => {
            const mapped = PROPERTY_TYPE_MAP[t]?.rightmove;
            return mapped ? mapped.split(',') : [];
          })
        ),
      ];
      if (rmTypes.length > 0) rmParams.set('propertyTypes', rmTypes.join(','));
    }
    if (selectedTenures.length > 0) {
      const rmTenures = selectedTenures.map((t) => (t === 'Freehold' ? 'FREEHOLD' : 'LEASEHOLD'));
      rmParams.set('tenureTypes', rmTenures.join(','));
    }
    rmParams.set('_includeSSTC', 'on');
    rightmove = `https://www.rightmove.co.uk/property-for-sale/find.html?${rmParams.toString()}`;
  }

  // OnTheMarket — postcode slug in URL path (e.g. "SW1A 1AA" → "sw1a-1aa")
  const otmSlug = postcode.toLowerCase().replace(/\s+/g, '-');
  const otmParams = new URLSearchParams();
  otmParams.set('radius', String(nearestRadius(radiusMiles, OTM_RADII)));
  if (minPrice !== undefined)
    otmParams.set('min-price', String(snapToAllowed(minPrice, OTM_PRICES, 'floor')));
  if (maxPrice !== undefined)
    otmParams.set('max-price', String(snapToAllowed(maxPrice, OTM_PRICES, 'ceil')));
  if (minBedrooms !== undefined) otmParams.set('min-bedrooms', String(minBedrooms));
  if (maxBedrooms !== undefined) otmParams.set('max-bedrooms', String(maxBedrooms));
  if (selectedTypes.length > 0) {
    const otmTypes = [
      ...new Set(selectedTypes.map((t) => PROPERTY_TYPE_MAP[t]?.onthemarket).filter(Boolean)),
    ];
    for (const ot of otmTypes) {
      otmParams.append('prop-types', ot!);
    }
  }
  otmParams.set('view', 'map-list');
  const onthemarket = `https://www.onthemarket.com/for-sale/property/${otmSlug}/?${otmParams.toString()}`;

  // Zoopla
  const zParams = new URLSearchParams();
  zParams.set('q', postcode);
  zParams.set('search_source', 'for-sale');
  zParams.set('radius', String(nearestRadius(radiusMiles, ZOOPLA_RADII)));
  if (minPrice !== undefined)
    zParams.set('price_min', String(snapToAllowed(minPrice, ZOOPLA_PRICES, 'floor')));
  if (maxPrice !== undefined)
    zParams.set('price_max', String(snapToAllowed(maxPrice, ZOOPLA_PRICES, 'ceil')));
  if (minBedrooms !== undefined) zParams.set('beds_min', String(minBedrooms));
  if (maxBedrooms !== undefined) zParams.set('beds_max', String(maxBedrooms));
  if (selectedTypes.length > 0) {
    const zTypes = [
      ...new Set(selectedTypes.map((t) => PROPERTY_TYPE_MAP[t]?.zoopla).filter(Boolean)),
    ];
    for (const zt of zTypes) {
      zParams.append('property_sub_type', zt!);
    }
  }
  const zoopla = `https://www.zoopla.co.uk/for-sale/property/?${zParams.toString()}`;

  return { rightmove, onthemarket, zoopla };
}
