export interface FieldDiff {
  key: string
  before: unknown
  after: unknown
}

export interface RowDiff {
  status: 'added' | 'removed' | 'changed' | 'unchanged'
  id?: string | number
  fields: FieldDiff[]
  before?: Record<string, unknown>
  after?: Record<string, unknown>
}

export interface TableDiff {
  tableName: string
  rows: RowDiff[]
  hasChanges: boolean
}

/**
 * Diff two arrays of row objects. Matches rows by `id` field if present,
 * otherwise by array position.
 */
export function diffTable(
  tableName: string,
  before: unknown[],
  after: unknown[],
): TableDiff {
  const beforeArr = (before || []) as Record<string, unknown>[]
  const afterArr = (after || []) as Record<string, unknown>[]

  const rows: RowDiff[] = []

  // Build lookup by id if available
  const hasIds = beforeArr.length > 0 && 'id' in (beforeArr[0] || {})

  if (hasIds) {
    const beforeMap = new Map(beforeArr.map(r => [String(r.id), r]))
    const afterMap = new Map(afterArr.map(r => [String(r.id), r]))
    const allIds = new Set([...beforeMap.keys(), ...afterMap.keys()])

    for (const id of allIds) {
      const b = beforeMap.get(id)
      const a = afterMap.get(id)

      if (!b && a) {
        rows.push({ status: 'added', id: a.id as string, fields: [], after: a })
      } else if (b && !a) {
        rows.push({ status: 'removed', id: b.id as string, fields: [], before: b })
      } else if (b && a) {
        const fields = diffFields(b, a)
        rows.push({
          status: fields.length > 0 ? 'changed' : 'unchanged',
          id: b.id as string,
          fields,
          before: b,
          after: a,
        })
      }
    }
  } else {
    // Positional comparison
    const maxLen = Math.max(beforeArr.length, afterArr.length)
    for (let i = 0; i < maxLen; i++) {
      const b = beforeArr[i]
      const a = afterArr[i]
      if (!b && a) {
        rows.push({ status: 'added', fields: [], after: a })
      } else if (b && !a) {
        rows.push({ status: 'removed', fields: [], before: b })
      } else if (b && a) {
        const fields = diffFields(b, a)
        rows.push({
          status: fields.length > 0 ? 'changed' : 'unchanged',
          fields,
          before: b,
          after: a,
        })
      }
    }
  }

  return {
    tableName,
    rows,
    hasChanges: rows.some(r => r.status !== 'unchanged'),
  }
}

function diffFields(before: Record<string, unknown>, after: Record<string, unknown>): FieldDiff[] {
  const diffs: FieldDiff[] = []
  const allKeys = new Set([...Object.keys(before), ...Object.keys(after)])
  for (const key of allKeys) {
    const bVal = JSON.stringify(before[key] ?? null)
    const aVal = JSON.stringify(after[key] ?? null)
    if (bVal !== aVal) {
      diffs.push({ key, before: before[key], after: after[key] })
    }
  }
  return diffs
}
