import { connectDB } from "@/lib/db";
import { PhotoMetadata } from "@/models/PhotoMetadata";
import { fetchOriginal } from "@/lib/nextcloud";
import sharp from "sharp";

// Gängige Foto-Druckformate (cm) — bewusst eine praxisnahe Auswahl statt
// jeder theoretisch möglichen Papiergröße, damit die Toleranzprüfung nicht
// versehentlich ein unpassendes, selten genutztes Format matcht.
const STANDARD_FORMATS_CM: [number, number][] = [
  [9, 13],
  [10, 15],
  [13, 18],
  [15, 20],
  [20, 30],
  [21, 29.7], // A4
  [30, 40],
  [30, 45],
];

const ASSUMED_DPI = 300;
// Wie nah BEIDE Maße (nicht nur das Seitenverhältnis!) an einem
// Standardformat liegen müssen, um dessen "hübschen" Namen zu zeigen statt
// der exakten Maße — siehe Rückfrage an Till (26.09.2026): bei zu großer
// Abweichung würde ein gerundetes Format beim Ausdrucken sichtbar
// beschneiden, ohne dass der Kunde das ahnt.
//
// Wichtig: reiner Seitenverhältnis-Vergleich reicht NICHT, weil mehrere
// Standardformate exakt dasselbe Verhältnis teilen (10×15, 20×30 und 30×45
// sind alle 2:3) — ein riesiges 50×34cm-Foto hätte sonst fälschlich als
// "10×15 cm" durchgehen können, nur weil das Seitenverhältnis passt. Daher
// wird auf die tatsächliche ABSOLUTE Größe geprüft, nicht nur die Form.
const SIZE_TOLERANCE = 0.08;

function fmtCm(cm: number): string {
  return cm.toLocaleString("de-DE", { minimumFractionDigits: 1, maximumFractionDigits: 1 });
}

// Rein rechnerisch, kein DB-Zugriff — leicht isoliert testbar.
export function computePrintFormatLabel(widthPx: number, heightPx: number, dpi = ASSUMED_DPI): string {
  const wCm = (widthPx / dpi) * 2.54;
  const hCm = (heightPx / dpi) * 2.54;

  let best: { stdW: number; stdH: number; deviation: number } | null = null;
  for (const [a, b] of STANDARD_FORMATS_CM) {
    // Beide Orientierungen prüfen (z. B. 30×20 ist dasselbe Format wie 20×30).
    for (const [stdW, stdH] of [[a, b] as const, [b, a] as const]) {
      const wDiff = Math.abs(wCm - stdW) / stdW;
      const hDiff = Math.abs(hCm - stdH) / stdH;
      if (wDiff > SIZE_TOLERANCE || hDiff > SIZE_TOLERANCE) continue;
      const deviation = wDiff + hDiff;
      if (!best || deviation < best.deviation) best = { stdW, stdH, deviation };
    }
  }
  if (best) return `${fmtCm(best.stdW)} × ${fmtCm(best.stdH)} cm`;
  return `${fmtCm(wCm)} × ${fmtCm(hCm)} cm`;
}

// Holt die Pixelmaße eines Fotos, gecacht in PhotoMetadata (einmalig aus
// Nextcloud gelesen + mit sharp ausgewertet statt bei jedem Seitenaufruf neu
// herunterzuladen/zu berechnen). Gibt null zurück, wenn die Datei nicht
// lesbar ist, statt die ganze Seite scheitern zu lassen.
export async function ensurePhotoDimensions(
  clientId: string,
  folderId: string,
  fileName: string,
  nextcloudPath: string,
  existing: { widthPx: number | null; heightPx: number | null } | undefined
): Promise<{ widthPx: number | null; heightPx: number | null }> {
  if (existing && existing.widthPx != null && existing.heightPx != null) {
    return existing;
  }
  await connectDB();
  try {
    const buffer = await fetchOriginal(`${nextcloudPath}/${fileName}`);
    const meta = await sharp(buffer).metadata();
    // sharp dreht die Ausgabe nicht automatisch — bei EXIF-Rotation um 90°/270°
    // sind width/height vertauscht gegenüber der tatsächlich angezeigten
    // Ausrichtung (.rotate() wird hier nicht angewendet, wir lesen nur Maße).
    const swapped = meta.orientation != null && meta.orientation >= 5;
    const widthPx = (swapped ? meta.height : meta.width) ?? null;
    const heightPx = (swapped ? meta.width : meta.height) ?? null;
    await PhotoMetadata.findOneAndUpdate(
      { folderId, fileName },
      { $set: { widthPx, heightPx }, $setOnInsert: { clientId, folderId, fileName } },
      { upsert: true }
    );
    return { widthPx, heightPx };
  } catch {
    return { widthPx: null, heightPx: null };
  }
}

// Tag-Vorschläge sind bewusst pro Ordner, nicht sitebreit (Tills expliziter
// Wunsch, 26.09.2026: "nur jeweils pro Fotogruppe/Ordner, nicht für alle
// Fotos zusammen") - es gibt keine feste globale Tag-Liste, die Vorschläge
// ergeben sich einfach aus bereits in diesem Ordner benutzten Tags.
export async function getFolderTagSuggestions(folderId: string): Promise<string[]> {
  await connectDB();
  const docs = await PhotoMetadata.find({ folderId, tags: { $exists: true, $ne: [] } }).select("tags").lean();
  return [...new Set(docs.flatMap((d) => d.tags))].sort((a, b) => a.localeCompare(b));
}
