import * as fs from 'fs';
import * as path from 'path';

export type ThemePreset =
  | 'corporate-blue' | 'minimal-mono' | 'vibrant'
  | 'academic' | 'dark' | 'warm-paper';

export interface ThemeOverrides {
  primary?: string; accent?: string; background?: string;
  text?: string; muted?: string;
  heading_font?: string; body_font?: string;
  title_size?: number; heading_size?: number; body_size?: number;
}

export type LayoutName =
  | 'title' | 'section' | 'bullets' | 'two-column'
  | 'image-right' | 'image-left' | 'image-full'
  | 'table' | 'chart' | 'quote' | 'closing' | 'custom';

export interface SlideEntry {
  layout: LayoutName;
  content: Record<string, unknown>;
  notes: string | null;
}

export interface SlidesDoc {
  version: 1;
  theme: { preset: ThemePreset; overrides: ThemeOverrides };
  slides: SlideEntry[];
}

export const DEFAULT_DOC: SlidesDoc = {
  version: 1,
  theme: { preset: 'minimal-mono', overrides: {} },
  slides: [],
};

const STATE_REL = 'output/.slides.json';

export class SlideStateSchemaError extends Error {
  constructor(msg: string) { super(msg); this.name = 'SlideStateSchemaError'; }
}

export function statePath(workspacePath: string): string {
  return path.join(workspacePath, STATE_REL);
}

export function readSlidesDoc(workspacePath: string): SlidesDoc {
  const p = statePath(workspacePath);
  if (!fs.existsSync(p)) return structuredClone(DEFAULT_DOC);
  let parsed: unknown;
  try { parsed = JSON.parse(fs.readFileSync(p, 'utf-8')); }
  catch (e) { throw new SlideStateSchemaError(`failed to parse ${STATE_REL}: ${(e as Error).message}`); }
  return validateDoc(parsed);
}

export function writeSlidesDoc(workspacePath: string, doc: SlidesDoc): void {
  const p = statePath(workspacePath);
  fs.mkdirSync(path.dirname(p), { recursive: true });
  fs.writeFileSync(p, JSON.stringify(doc, null, 2), 'utf-8');
}

export function appendSlide(workspacePath: string, entry: SlideEntry): void {
  const doc = readSlidesDoc(workspacePath);
  doc.slides.push(entry);
  writeSlidesDoc(workspacePath, doc);
}

export function setTheme(
  workspacePath: string,
  theme: { preset: ThemePreset; overrides: ThemeOverrides },
): void {
  const doc = readSlidesDoc(workspacePath);
  doc.theme = theme;
  writeSlidesDoc(workspacePath, doc);
}

export function resetSlides(workspacePath: string): void {
  const doc = readSlidesDoc(workspacePath);
  doc.slides = [];
  writeSlidesDoc(workspacePath, doc);
}

const VALID_PRESETS: ThemePreset[] = [
  'corporate-blue', 'minimal-mono', 'vibrant',
  'academic', 'dark', 'warm-paper',
];

const VALID_LAYOUTS: LayoutName[] = [
  'title', 'section', 'bullets', 'two-column',
  'image-right', 'image-left', 'image-full',
  'table', 'chart', 'quote', 'closing', 'custom',
];

function validateDoc(raw: unknown): SlidesDoc {
  if (!raw || typeof raw !== 'object') throw new SlideStateSchemaError('not an object');
  const d = raw as Record<string, unknown>;
  if (d.version !== 1) throw new SlideStateSchemaError(`unsupported version: ${String(d.version)}`);
  const theme = d.theme as Record<string, unknown> | undefined;
  if (!theme || typeof theme !== 'object') throw new SlideStateSchemaError('theme missing');
  if (!VALID_PRESETS.includes(theme.preset as ThemePreset))
    throw new SlideStateSchemaError(`unknown theme preset: ${String(theme.preset)}`);
  if (!theme.overrides || typeof theme.overrides !== 'object')
    throw new SlideStateSchemaError('theme.overrides missing');
  if (!Array.isArray(d.slides)) throw new SlideStateSchemaError('slides must be an array');
  for (const [i, s] of (d.slides as unknown[]).entries()) {
    if (!s || typeof s !== 'object') throw new SlideStateSchemaError(`slides[${i}] not object`);
    const e = s as Record<string, unknown>;
    if (!VALID_LAYOUTS.includes(e.layout as LayoutName))
      throw new SlideStateSchemaError(`slides[${i}].layout invalid: ${String(e.layout)}`);
    if (!e.content || typeof e.content !== 'object')
      throw new SlideStateSchemaError(`slides[${i}].content must be object`);
  }
  return d as unknown as SlidesDoc;
}
