import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import * as fs from 'fs';
import * as path from 'path';
import { tmpdir } from 'os';
import { executeAddSlide } from './add-slide.js';
import { readSlidesDoc } from './state.js';

const ctx = (ws: string) => ({ workspacePath: ws } as any);

describe('AddSlide validator', () => {
  let ws: string;
  beforeEach(() => { ws = fs.mkdtempSync(path.join(tmpdir(), 'slide-as-')); });
  afterEach(() => { fs.rmSync(ws, { recursive: true, force: true }); });

  it('rejects unknown layout', async () => {
    const r = await executeAddSlide({ layout: 'foo', content: {} }, ctx(ws));
    expect(r.isError).toBe(true);
    expect(r.output).toMatch(/layout/i);
  });

  it('title: requires title', async () => {
    expect((await executeAddSlide({ layout: 'title', content: {} }, ctx(ws))).isError).toBe(true);
    const r = await executeAddSlide({ layout: 'title', content: { title: 'A' } }, ctx(ws));
    expect(r.isError).toBeFalsy();
    expect(readSlidesDoc(ws).slides).toHaveLength(1);
  });

  it('bullets: requires title and bullets[]', async () => {
    expect((await executeAddSlide({ layout: 'bullets', content: { title: 'A' } }, ctx(ws))).isError).toBe(true);
    const r = await executeAddSlide(
      { layout: 'bullets', content: { title: 'A', bullets: ['x', 'y'] }, notes: 'n' },
      ctx(ws),
    );
    expect(r.isError).toBeFalsy();
    expect(readSlidesDoc(ws).slides[0].notes).toBe('n');
  });

  it('chart: rejects mismatched series.values.length vs categories.length', async () => {
    const r = await executeAddSlide({
      layout: 'chart',
      content: {
        title: 'X',
        chart_type: 'bar',
        data: { categories: ['a', 'b', 'c'], series: [{ name: 's', values: [1, 2] }] },
      },
    }, ctx(ws));
    expect(r.isError).toBe(true);
    expect(r.output).toMatch(/length/i);
  });

  it('chart: accepts matched lengths', async () => {
    const r = await executeAddSlide({
      layout: 'chart',
      content: {
        title: 'X',
        chart_type: 'line',
        data: { categories: ['a', 'b'], series: [{ name: 's', values: [1, 2] }] },
      },
    }, ctx(ws));
    expect(r.isError).toBeFalsy();
  });

  it('image-right: rejects when image.path is outside workspace', async () => {
    const r = await executeAddSlide({
      layout: 'image-right',
      content: { title: 'X', body: 'hi', image: { path: '../etc/passwd' } },
    }, ctx(ws));
    expect(r.isError).toBe(true);
    expect(r.output).toMatch(/path/i);
  });

  it('custom: rejects when elements missing required coords', async () => {
    const r = await executeAddSlide({
      layout: 'custom',
      content: { elements: [{ type: 'text', text: 'hi' }] },
    }, ctx(ws));
    expect(r.isError).toBe(true);
    expect(r.output).toMatch(/x|y|w|h/i);
  });

  it('custom: accepts elements with full coords', async () => {
    const r = await executeAddSlide({
      layout: 'custom',
      content: { elements: [{ type: 'text', text: 'hi', x: 1, y: 1, w: 4, h: 1 }] },
    }, ctx(ws));
    expect(r.isError).toBeFalsy();
  });

  it('table: requires headers + rows[]', async () => {
    expect((await executeAddSlide({ layout: 'table', content: { title: 'T', headers: ['a'] } }, ctx(ws))).isError).toBe(true);
    const r = await executeAddSlide({
      layout: 'table',
      content: { title: 'T', headers: ['a', 'b'], rows: [['1', '2'], ['3', '4']] },
    }, ctx(ws));
    expect(r.isError).toBeFalsy();
  });

  it('section: requires title', async () => {
    expect((await executeAddSlide({ layout: 'section', content: {} }, ctx(ws))).isError).toBe(true);
    expect((await executeAddSlide({ layout: 'section', content: { title: '1. 背景' } }, ctx(ws))).isError).toBeFalsy();
  });

  it('two-column: requires title + left/right', async () => {
    expect((await executeAddSlide({ layout: 'two-column', content: { title: 'A' } }, ctx(ws))).isError).toBe(true);
    expect((await executeAddSlide({
      layout: 'two-column',
      content: { title: 'A', left: { heading: 'L', bullets: ['1'] }, right: { heading: 'R', bullets: ['2'] } },
    }, ctx(ws))).isError).toBeFalsy();
  });

  it('image-full: requires image.path', async () => {
    expect((await executeAddSlide({ layout: 'image-full', content: {} }, ctx(ws))).isError).toBe(true);
  });

  it('quote: requires quote', async () => {
    expect((await executeAddSlide({ layout: 'quote', content: {} }, ctx(ws))).isError).toBe(true);
    expect((await executeAddSlide({ layout: 'quote', content: { quote: 'hi' } }, ctx(ws))).isError).toBeFalsy();
  });

  it('closing: works with empty content', async () => {
    const r = await executeAddSlide({ layout: 'closing', content: {} }, ctx(ws));
    expect(r.isError).toBeFalsy();
  });
});
