import { eq, desc, and, asc } from 'drizzle-orm';
import { db } from './client';
import { campaigns, campaignVersions, type CampaignVersion } from './schema';

/**
 * Versions-Snapshot eines Drafts. Komplette Snapshots:
 *  - source='auto'      → vom System (5min-Heartbeat)
 *  - source='milestone' → Generate, Re-Render, Send
 *  - source='manual'    → User-Klick mit Label
 *
 * Cleanup-Strategie: keep last 50 'auto' versions per draft, alle 'manual'/'milestone' bleiben.
 */

const AUTO_KEEP = 50;

export type VersionSource = 'auto' | 'manual' | 'milestone';
export type VersionMilestone = 'generate' | 'render' | 'send' | 'pre_restore';

export interface VersionInput {
  campaignId: string;
  source: VersionSource;
  label?: string;
  milestone?: VersionMilestone;
}

export interface VersionSummary {
  id: string;
  campaignId: string;
  source: VersionSource;
  label: string | null;
  milestone: VersionMilestone | null;
  name: string;
  createdAt: Date;
  hasOutput: boolean;
}

/**
 * Erzeugt einen Snapshot des aktuellen Campaign-Stands. Liest die Campaign-Row
 * frisch, damit sicher der zuletzt gespeicherte Stand drin landet (nicht ein veralteter
 * Frontend-Snapshot, der schon wieder vom Auto-Save überholt wurde).
 */
export function createVersion(input: VersionInput): CampaignVersion {
  const c = db.select().from(campaigns).where(eq(campaigns.id, input.campaignId)).get();
  if (!c) throw new Error(`Campaign not found: ${input.campaignId}`);

  const id = crypto.randomUUID();
  db.insert(campaignVersions).values({
    id,
    campaignId: input.campaignId,
    source: input.source,
    label: input.label,
    milestone: input.milestone,
    name: c.name,
    briefingJson: c.briefingJson,
    tiptapJson: c.tiptapJson,
    strategyJson: c.strategyJson,
    emailHtml: c.emailHtmlPath, // Wir speichern hier den HTML-Inhalt nicht direkt,
    // da der Output über strategyJson._generated.emailHtml mitläuft. Pfad genügt.
    emailText: c.emailTxtPath,
    spamReportJson: c.spamReportJson,
    heroAssetId: c.heroAssetId,
  }).run();

  // Cleanup nur für 'auto' — manual/milestone bleiben unbegrenzt
  if (input.source === 'auto') {
    pruneAutoVersions(input.campaignId, AUTO_KEEP);
  }

  return db.select().from(campaignVersions).where(eq(campaignVersions.id, id)).get()!;
}

export function listVersions(campaignId: string): VersionSummary[] {
  const rows = db.select()
    .from(campaignVersions)
    .where(eq(campaignVersions.campaignId, campaignId))
    .orderBy(desc(campaignVersions.createdAt))
    .all();

  return rows.map((v) => ({
    id: v.id,
    campaignId: v.campaignId,
    source: v.source as VersionSource,
    label: v.label,
    milestone: v.milestone as VersionMilestone | null,
    name: v.name,
    createdAt: v.createdAt,
    hasOutput: !!v.strategyJson,
  }));
}

export function getVersion(id: string): CampaignVersion | undefined {
  return db.select().from(campaignVersions).where(eq(campaignVersions.id, id)).get();
}

/**
 * Stellt eine Version wieder her. Erzeugt VOR dem Restore einen automatischen
 * 'pre_restore'-Snapshot — so geht der Ist-Stand nie verloren, der User kann
 * jederzeit zurück.
 *
 * Returnt die wiederhergestellte (jetzt aktuelle) Campaign.
 */
export function restoreVersion(versionId: string): { campaignId: string } {
  const v = getVersion(versionId);
  if (!v) throw new Error(`Version not found: ${versionId}`);

  return db.transaction((tx) => {
    // 1. Pre-Restore-Snapshot des aktuellen Stands (von Hand, weil createVersion
    //    in eigener Transaktion läuft und wir hier konsistent bleiben wollen)
    const c = tx.select().from(campaigns).where(eq(campaigns.id, v.campaignId)).get();
    if (!c) throw new Error('Campaign disappeared mid-restore');

    tx.insert(campaignVersions).values({
      id: crypto.randomUUID(),
      campaignId: v.campaignId,
      source: 'manual',
      label: `Vor Restore: ${formatVersionLabel(v)}`,
      milestone: 'pre_restore',
      name: c.name,
      briefingJson: c.briefingJson,
      tiptapJson: c.tiptapJson,
      strategyJson: c.strategyJson,
      emailHtml: c.emailHtmlPath,
      emailText: c.emailTxtPath,
      spamReportJson: c.spamReportJson,
      heroAssetId: c.heroAssetId,
    }).run();

    // 2. Restore: kopiere Version-Snapshot zurück in Campaign-Row
    tx.update(campaigns).set({
      name: v.name,
      briefingJson: v.briefingJson,
      tiptapJson: v.tiptapJson,
      strategyJson: v.strategyJson,
      spamReportJson: v.spamReportJson,
      heroAssetId: v.heroAssetId,
      updatedAt: new Date(),
    }).where(eq(campaigns.id, v.campaignId)).run();

    return { campaignId: v.campaignId };
  });
}

/** Behält die letzten `keepN` Auto-Versionen pro Draft, löscht den Rest. Manual/Milestone bleiben. */
export function pruneAutoVersions(campaignId: string, keepN: number = AUTO_KEEP): number {
  const autoVersions = db.select()
    .from(campaignVersions)
    .where(and(
      eq(campaignVersions.campaignId, campaignId),
      eq(campaignVersions.source, 'auto'),
    ))
    .orderBy(asc(campaignVersions.createdAt))
    .all();

  const toDelete = autoVersions.slice(0, Math.max(0, autoVersions.length - keepN));
  let deleted = 0;
  for (const v of toDelete) {
    const r = db.delete(campaignVersions).where(eq(campaignVersions.id, v.id)).run();
    deleted += r.changes;
  }
  return deleted;
}

/**
 * Gibt den Zeitpunkt der jüngsten Auto/Milestone-Version zurück (für die 5min-Heartbeat-Logik
 * im Auto-Save-Hook). Manual zählt nicht — User-Snapshots sollen keine Auto-Snapshots blockieren.
 */
export function getLastAutoVersionAt(campaignId: string): Date | null {
  const v = db.select()
    .from(campaignVersions)
    .where(and(
      eq(campaignVersions.campaignId, campaignId),
      eq(campaignVersions.source, 'auto'),
    ))
    .orderBy(desc(campaignVersions.createdAt))
    .limit(1)
    .get();
  return v?.createdAt ?? null;
}

function formatVersionLabel(v: CampaignVersion): string {
  if (v.label) return v.label;
  if (v.milestone) return v.milestone;
  return new Date(v.createdAt).toLocaleString('de-DE');
}
