import type { Property, CustomerSearch } from "./appwrite/schema";

const DEFAULT_WEIGHT = 3;

export const ROOM_ORDER = [
  "Stüdyo",
  "1+0",
  "1+1",
  "2+1",
  "3+1",
  "4+1",
  "4+2",
  "5+1",
  "5+2",
  "6+",
];

export interface CriterionResult {
  label: string;
  weight: number;
  ratio: number;
  earned: number;
  note: string;
}

export interface ScoreBreakdown {
  total: number;
  maxPossible: number;
  score: number;
  criteria: CriterionResult[];
}

export function tryParseJsonArray(json: string): string[] {
  try {
    const arr = JSON.parse(json);
    return Array.isArray(arr) ? arr : [];
  } catch {
    return [];
  }
}

function roomCountRatio(
  roomCount: string | undefined | null,
  desired: string[],
): { ratio: number; note: string } {
  if (!roomCount) return { ratio: 0.5, note: "İlanda oda sayısı belirtilmemiş" };
  if (desired.includes(roomCount)) return { ratio: 1.0, note: `${roomCount} tam eşleşme` };

  const propIdx = ROOM_ORDER.indexOf(roomCount);
  if (propIdx === -1) return { ratio: 0.0, note: `${roomCount} listede yok` };

  let minDist = Infinity;
  for (const d of desired) {
    const dIdx = ROOM_ORDER.indexOf(d);
    if (dIdx !== -1) minDist = Math.min(minDist, Math.abs(propIdx - dIdx));
  }

  if (minDist === 1) return { ratio: 0.5, note: `${roomCount} — yakın oda sayısı (1 adım)` };
  if (minDist === 2) return { ratio: 0.25, note: `${roomCount} — yakın oda sayısı (2 adım)` };
  return { ratio: 0.0, note: `${roomCount} istenenden çok farklı` };
}

function priceRatio(
  price: number,
  minPrice?: number | null,
  maxPrice?: number | null,
): { ratio: number; note: string } {
  const fmt = (n: number) => n.toLocaleString("tr-TR");
  if (minPrice != null && price < minPrice) {
    return { ratio: 0.9, note: `${fmt(price)} ₺ — minimum bütçenin altında` };
  }
  if (maxPrice != null && price > maxPrice) {
    const overage = ((price - maxPrice) / maxPrice) * 100;
    if (overage <= 10) return { ratio: 0.7, note: `${fmt(price)} ₺ — bütçenin %${overage.toFixed(0)} üstünde` };
    if (overage <= 20) return { ratio: 0.4, note: `${fmt(price)} ₺ — bütçenin %${overage.toFixed(0)} üstünde` };
    if (overage <= 30) return { ratio: 0.2, note: `${fmt(price)} ₺ — bütçenin %${overage.toFixed(0)} üstünde` };
    return { ratio: 0.0, note: `${fmt(price)} ₺ — bütçeyi çok aşıyor (%${overage.toFixed(0)})` };
  }
  const range = [minPrice != null ? `min ${fmt(minPrice)}` : null, maxPrice != null ? `max ${fmt(maxPrice)}` : null]
    .filter(Boolean)
    .join(" – ");
  return { ratio: 1.0, note: `${fmt(price)} ₺ — bütçe uygun (${range})` };
}

function m2Ratio(
  m2: number,
  minM2?: number | null,
  maxM2?: number | null,
): { ratio: number; note: string } {
  if (minM2 != null && m2 < minM2) {
    const shortage = ((minM2 - m2) / minM2) * 100;
    if (shortage <= 10) return { ratio: 0.7, note: `${m2} m² — minimum m²'nin %${shortage.toFixed(0)} altında` };
    if (shortage <= 20) return { ratio: 0.4, note: `${m2} m² — minimum m²'nin %${shortage.toFixed(0)} altında` };
    return { ratio: 0.0, note: `${m2} m² — minimum m²'den çok küçük` };
  }
  if (maxM2 != null && m2 > maxM2) {
    return { ratio: 0.9, note: `${m2} m² — maksimumdan büyük ama kabul edilebilir` };
  }
  return { ratio: 1.0, note: `${m2} m² — m² uygun` };
}

function locationScore(
  property: Property,
  search: CustomerSearch,
): { ratio: number; note: string } {
  const cities = tryParseJsonArray(search.cities ?? "");
  const districts = tryParseJsonArray(search.districts ?? "");

  const cityMatch =
    cities.length === 0 ||
    cities.some((c) => c.toLowerCase() === property.city.toLowerCase());

  if (!cityMatch) {
    return { ratio: 0.0, note: `${property.city} — istenen şehirlerde yok (${cities.join(", ")})` };
  }

  if (districts.length === 0) {
    return { ratio: 1.0, note: `${property.city} — şehir eşleşiyor` };
  }

  const districtMatch =
    property.district &&
    districts.some((d) => d.toLowerCase() === property.district!.toLowerCase());

  if (districtMatch) {
    return { ratio: 1.0, note: `${property.district}, ${property.city} — tam konum eşleşmesi` };
  }

  return {
    ratio: 0.4,
    note: `${property.city} — şehir eşleşiyor ama ${property.district ?? "ilçe yok"} istenenden farklı (${districts.join(", ")})`,
  };
}

export function scoreMatchBreakdown(property: Property, search: CustomerSearch): ScoreBreakdown {
  const criteria: CriterionResult[] = [];
  let total = 0;
  let maxPossible = 0;

  function addCriterion(
    label: string,
    weight: number | undefined | null,
    ratio: number,
    note: string,
  ) {
    const wt = weight ?? DEFAULT_WEIGHT;
    const earned = wt * ratio;
    total += earned;
    maxPossible += wt;
    criteria.push({ label, weight: wt, ratio, earned, note });
  }

  const propTypes = search.propertyTypes ? tryParseJsonArray(search.propertyTypes) : [];
  if (propTypes.length > 0) {
    const matches = propTypes.includes(property.propertyType);
    addCriterion(
      "Emlak tipi",
      search.propertyTypeWeight,
      matches ? 1.0 : 0.0,
      matches
        ? `${property.propertyType} — tam eşleşme`
        : `${property.propertyType} — istenmiyor (${propTypes.join(", ")})`,
    );
  }

  const roomCounts = search.roomCounts ? tryParseJsonArray(search.roomCounts) : [];
  if (roomCounts.length > 0) {
    const { ratio, note } = roomCountRatio(property.roomCount, roomCounts);
    addCriterion("Oda sayısı", search.roomCountWeight, ratio, note);
  }

  if (search.minPrice != null || search.maxPrice != null) {
    const { ratio, note } = priceRatio(property.price, search.minPrice, search.maxPrice);
    addCriterion("Fiyat", search.priceWeight, ratio, note);
  }

  if (search.minM2 != null || search.maxM2 != null) {
    const m2 = property.netM2 ?? property.grossM2;
    if (m2 != null) {
      const { ratio, note } = m2Ratio(m2, search.minM2, search.maxM2);
      addCriterion("m²", search.m2Weight, ratio, note);
    } else {
      addCriterion("m²", search.m2Weight, 0.5, "İlanda m² bilgisi yok");
    }
  }

  const cities = search.cities ? tryParseJsonArray(search.cities) : [];
  if (cities.length > 0) {
    const { ratio, note } = locationScore(property, search);
    addCriterion("Konum", search.locationWeight, ratio, note);
  }

  const score =
    maxPossible === 0 ? 100 : Math.round((total / maxPossible) * 100);

  return { total, maxPossible, score, criteria };
}

export function scoreMatch(property: Property, search: CustomerSearch): number {
  return scoreMatchBreakdown(property, search).score;
}
