function levenshteinDistance(a: string, b: string): number {
  const an = a.length;
  const bn = b.length;
  const matrix: number[][] = [];

  for (let i = 0; i <= an; i++) {
    matrix[i] = [i];
  }
  for (let j = 0; j <= bn; j++) {
    matrix[0][j] = j;
  }

  for (let i = 1; i <= an; i++) {
    for (let j = 1; j <= bn; j++) {
      const cost = a[i - 1] === b[j - 1] ? 0 : 1;
      matrix[i][j] = Math.min(
        matrix[i - 1][j] + 1,
        matrix[i][j - 1] + 1,
        matrix[i - 1][j - 1] + cost,
      );
    }
  }

  return matrix[an][bn];
}

export function fuzzyMatchNames(name1: string, name2: string): { match: boolean; distance: number; similarity: number } {
  const n1 = name1.toLowerCase().trim();
  const n2 = name2.toLowerCase().trim();
  const distance = levenshteinDistance(n1, n2);
  const maxLen = Math.max(n1.length, n2.length);
  const similarity = maxLen > 0 ? 1 - distance / maxLen : 1;
  return { match: similarity >= 0.8, distance, similarity };
}

export type ChangeType = "ownership_transfer" | "lien_filing" | "tax_change" | "deed_change" | "metadata_change";
export type SeverityLevel = "info" | "warning" | "critical";

export interface SnapshotData {
  ownerName: string;
  deedDate?: string | null;
  taxAmount?: number | null;
  lienCount: number;
  propertyType?: string;
  taxId?: string | null;
}

export interface DetectedChange {
  changeType: ChangeType;
  severity: SeverityLevel;
  details: Record<string, unknown>;
}

export function severityForChange(changeType: ChangeType, magnitude: number): SeverityLevel {
  switch (changeType) {
    case "ownership_transfer":
      return "critical";
    case "lien_filing":
      return magnitude > 0 ? "critical" : "warning";
    case "tax_change":
      return Math.abs(magnitude) > 0.2 ? "warning" : "info";
    case "deed_change":
      return "warning";
    case "metadata_change":
      return "info";
  }
}

export function detectChanges(oldSnapshot: SnapshotData | null, newData: SnapshotData): DetectedChange[] {
  const changes: DetectedChange[] = [];

  if (!oldSnapshot) {
    return changes;
  }

  const ownerMatch = fuzzyMatchNames(oldSnapshot.ownerName, newData.ownerName);
  if (!ownerMatch.match) {
    changes.push({
      changeType: "ownership_transfer",
      severity: severityForChange("ownership_transfer", 0),
      details: {
        previousOwner: oldSnapshot.ownerName,
        newOwner: newData.ownerName,
        nameSimilarity: ownerMatch.similarity,
      },
    });
  }

  if (newData.lienCount > oldSnapshot.lienCount) {
    changes.push({
      changeType: "lien_filing",
      severity: severityForChange("lien_filing", newData.lienCount - oldSnapshot.lienCount),
      details: {
        previousLienCount: oldSnapshot.lienCount,
        newLienCount: newData.lienCount,
        newLiens: newData.lienCount - oldSnapshot.lienCount,
      },
    });
  }

  if (oldSnapshot.deedDate !== newData.deedDate && newData.deedDate) {
    changes.push({
      changeType: "deed_change",
      severity: severityForChange("deed_change", 0),
      details: {
        previousDeedDate: oldSnapshot.deedDate,
        newDeedDate: newData.deedDate,
      },
    });
  }

  if (oldSnapshot.taxAmount !== newData.taxAmount && newData.taxAmount != null && oldSnapshot.taxAmount != null) {
    const magnitude = (newData.taxAmount - oldSnapshot.taxAmount) / oldSnapshot.taxAmount;
    changes.push({
      changeType: "tax_change",
      severity: severityForChange("tax_change", magnitude),
      details: {
        previousTaxAmount: oldSnapshot.taxAmount,
        newTaxAmount: newData.taxAmount,
        changePercent: magnitude,
      },
    });
  }

  if (oldSnapshot.propertyType !== newData.propertyType || oldSnapshot.taxId !== newData.taxId) {
    changes.push({
      changeType: "metadata_change",
      severity: severityForChange("metadata_change", 0),
      details: {
        previousPropertyType: oldSnapshot.propertyType,
        newPropertyType: newData.propertyType,
        previousTaxId: oldSnapshot.taxId,
        newTaxId: newData.taxId,
      },
    });
  }

  return changes;
}
