import {
  DocumentType,
  ProcessedReceipt,
  ReceiptCategory,
  ReceiptData,
} from "../schema/receipt";
import { normalizePaymentMethod } from "../parse/paymentMethod";

/**
 * SHA-256 hex is 64 chars; multi-page PDFs append `_p{n}` (up to `_p20` → 68).
 * Matches `receipts.image_hash varchar(128)` and the sanitize identifier cap.
 */
export const IMAGE_HASH_MAX_LENGTH = 128;

/** Page-scoped hash so duplicate detection stays meaningful for multi-page PDFs. */
export function pageImageHash(sourceSha256: string, pageNumber: number, pageCount: number): string {
  if (pageCount <= 1) return sourceSha256;
  return `${sourceSha256}_p${pageNumber}`;
}

/**
 * Extraction blob persisted in `receipts.extraction_json`.
 *
 * `previewUrl` stays in `storage_url` only — putting the data-URL in both
 * columns would double row size for no gain.
 */
export function toExtractionJson(
  receipt: ProcessedReceipt | (ReceiptData & Partial<ProcessedReceipt>)
): Record<string, unknown> {
  const { previewUrl: _previewUrl, ...rest } = receipt as ProcessedReceipt & {
    previewUrl?: string;
  };
  return { ...rest };
}

export interface ReceiptDbRow {
  id: string;
  projectId: string | null;
  imageHash: string;
  storageUrl: string | null;
  merchantName: string | null;
  receiptDate: string | null;
  receiptNumber: string | null;
  documentType: string | null;
  category: string | null;
  currency: string | null;
  totalAmount: string | number;
  netAmount: string | number | null;
  tipAmount: string | number | null;
  taxBreakdownJson: unknown;
  lineItemsJson: unknown;
  validationJson: unknown;
  rawOcrText: string | null;
  paymentMethod: string | null;
  isMathValid: boolean;
  needsReview: boolean;
  createdAt: Date | string | null;
  updatedAt: Date | string | null;
  extractionJson?: unknown;
}

function parseAmount(value: string | number | null | undefined): number | null {
  if (value === null || value === undefined || value === "") return null;
  const n = typeof value === "number" ? value : parseFloat(value);
  return Number.isFinite(n) ? n : null;
}

function isoTimestamp(value: Date | string | null | undefined): string {
  if (!value) return new Date().toISOString();
  if (value instanceof Date) return value.toISOString();
  const parsed = new Date(value);
  return Number.isNaN(parsed.getTime()) ? new Date().toISOString() : parsed.toISOString();
}

function todayIsoDate(): string {
  return new Date().toISOString().split("T")[0];
}

/**
 * Reconstructs a {@link ProcessedReceipt} from a database row.
 *
 * Prefer `extraction_json` (full scanner output: address, tax id, time,
 * hospitality, confidence, original filename). Fall back to the indexed
 * columns for rows written before that column existed.
 */
export function dbRowToProcessedReceipt(
  row: ReceiptDbRow,
  options: { includePreview?: boolean } = {}
): ProcessedReceipt {
  const blob =
    row.extractionJson && typeof row.extractionJson === "object"
      ? (row.extractionJson as Partial<ProcessedReceipt>)
      : {};

  const legacy: ProcessedReceipt = {
    id: row.id,
    projectId: row.projectId,
    merchant: {
      name: row.merchantName || "Unbekannter Händler",
      address: null,
      taxId: null,
      confidence: 1.0,
    },
    date: {
      isoDate: row.receiptDate || todayIsoDate(),
      time: null,
      confidence: 1.0,
    },
    documentType: (row.documentType as DocumentType) || "KASSENBON",
    receiptNumber: row.receiptNumber,
    currency: row.currency || "EUR",
    totalAmount: {
      value: parseAmount(row.totalAmount) ?? 0,
      confidence: 1.0,
    },
    netAmount: parseAmount(row.netAmount),
    tipAmount: parseAmount(row.tipAmount),
    taxBreakdown: Array.isArray(row.taxBreakdownJson) ? row.taxBreakdownJson : [],
    lineItems: Array.isArray(row.lineItemsJson) ? row.lineItemsJson : [],
    suggestedCategory: (row.category as ReceiptCategory) || "Sonstiges",
    validation: (row.validationJson as ProcessedReceipt["validation"]) || {
      isMathValid: row.isMathValid,
      isDuplicateSuspected: false,
      needsUserReview: row.needsReview,
      reviewField: "none",
      reviewReason: null,
    },
    imageHash: row.imageHash,
    originalFileName: row.merchantName ? `${row.merchantName}.jpg` : "receipt.jpg",
    fileSizeBytes: 0,
    previewUrl: row.storageUrl || undefined,
    createdAt: isoTimestamp(row.createdAt),
    updatedAt: isoTimestamp(row.updatedAt),
    status: row.needsReview ? "needs_review" : "ready",
    rawText: row.rawOcrText || undefined,
    paymentMethod: normalizePaymentMethod(row.paymentMethod) ?? undefined,
  };

  const merged: ProcessedReceipt = {
    ...legacy,
    ...blob,
    id: row.id,
    projectId: row.projectId,
    imageHash: row.imageHash,
    previewUrl: row.storageUrl || blob.previewUrl || legacy.previewUrl,
    createdAt: legacy.createdAt,
    updatedAt: legacy.updatedAt,
  };

  if (row.needsReview) {
    merged.status = "needs_review";
  } else if (
    merged.status === "pending" ||
    merged.status === "processing" ||
    merged.status === "error"
  ) {
    // keep transient statuses from the blob
  } else {
    merged.status = "ready";
  }

  if (options.includePreview === false) {
    merged.previewUrl = undefined;
  }

  return merged;
}
