import { ToolDef } from '../../../llm/openai-compat.js';
import type { ToolContext, ToolResult } from '../core.js';
import { resolveAndGuard } from '../core.js';
import { appendSlide, type LayoutName, type SlideEntry } from './state.js';

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

const VALID_CHART_TYPES = ['bar', 'line', 'pie', 'doughnut', 'area', 'scatter'];
const VALID_CUSTOM_TYPES = ['text', 'image', 'shape', 'table', 'chart'];

export const ADD_SLIDE_DEF: ToolDef = {
  type: 'function',
  function: {
    name: 'AddSlide',
    description:
      'pptxgenjs スライドを 1 枚追加する。layout を選び content をレイアウト依存の形で渡す。詳細は ReadToolDoc({ name: "AddSlide" })。',
    parameters: {
      type: 'object',
      properties: {
        layout: { type: 'string', enum: VALID_LAYOUTS },
        content: { type: 'object' },
        notes: { type: 'string' },
      },
      required: ['layout', 'content'],
    },
  },
};

export async function executeAddSlide(
  input: Record<string, unknown>,
  ctx: ToolContext,
): Promise<ToolResult> {
  const layout = input['layout'];
  const content = input['content'];
  const notes = typeof input['notes'] === 'string' ? input['notes'] : null;

  if (typeof layout !== 'string' || !VALID_LAYOUTS.includes(layout as LayoutName))
    return { output: `AddSlide error: layout must be one of ${VALID_LAYOUTS.join(', ')}`, isError: true };
  if (!content || typeof content !== 'object')
    return { output: 'AddSlide error: content must be an object', isError: true };

  try {
    validateContent(layout as LayoutName, content as Record<string, unknown>, ctx);
  } catch (e) {
    return { output: `AddSlide error: ${(e as Error).message}`, isError: true };
  }

  const entry: SlideEntry = { layout: layout as LayoutName, content: content as Record<string, unknown>, notes };
  try { appendSlide(ctx.workspacePath, entry); }
  catch (e) { return { output: `AddSlide error: ${(e as Error).message}`, isError: true }; }
  return { output: `Added slide #${entry.layout}.`, isError: false };
}

function requireString(obj: Record<string, unknown>, key: string, label: string): void {
  if (typeof obj[key] !== 'string' || !obj[key]) throw new Error(`${label}.${key} (string) is required`);
}
function requireArray(obj: Record<string, unknown>, key: string, label: string): void {
  if (!Array.isArray(obj[key])) throw new Error(`${label}.${key} (array) is required`);
}
function checkImagePath(workspacePath: string, obj: Record<string, unknown>, label: string): void {
  const img = obj['image'] as Record<string, unknown> | undefined;
  if (!img || typeof img['path'] !== 'string') throw new Error(`${label}.image.path is required`);
  try { resolveAndGuard(workspacePath, img['path']); }
  catch (e) { throw new Error(`${label}.image.path: ${(e as Error).message}`); }
}

function validateContent(layout: LayoutName, c: Record<string, unknown>, ctx: ToolContext): void {
  switch (layout) {
    case 'title':
      requireString(c, 'title', 'content');
      return;
    case 'section':
      requireString(c, 'title', 'content');
      return;
    case 'bullets':
      requireString(c, 'title', 'content');
      requireArray(c, 'bullets', 'content');
      return;
    case 'two-column':
      requireString(c, 'title', 'content');
      if (!c['left'] || typeof c['left'] !== 'object') throw new Error('content.left is required');
      if (!c['right'] || typeof c['right'] !== 'object') throw new Error('content.right is required');
      return;
    case 'image-right':
    case 'image-left':
      requireString(c, 'title', 'content');
      checkImagePath(ctx.workspacePath, c, 'content');
      return;
    case 'image-full':
      checkImagePath(ctx.workspacePath, c, 'content');
      return;
    case 'table': {
      requireString(c, 'title', 'content');
      requireArray(c, 'headers', 'content');
      requireArray(c, 'rows', 'content');
      const headers = c['headers'] as unknown[];
      for (const [i, row] of (c['rows'] as unknown[]).entries()) {
        if (!Array.isArray(row)) throw new Error(`content.rows[${i}] must be an array`);
        if (row.length !== headers.length) throw new Error(`content.rows[${i}] length (${row.length}) != headers (${headers.length})`);
      }
      const cw = c['col_widths'];
      if (cw !== undefined) {
        if (!Array.isArray(cw) || cw.length !== headers.length)
          throw new Error('content.col_widths length must match headers length');
      }
      return;
    }
    case 'chart': {
      requireString(c, 'title', 'content');
      if (typeof c['chart_type'] !== 'string' || !VALID_CHART_TYPES.includes(c['chart_type']))
        throw new Error(`content.chart_type must be one of ${VALID_CHART_TYPES.join(', ')}`);
      const data = c['data'] as Record<string, unknown> | undefined;
      if (!data) throw new Error('content.data is required');
      if (!Array.isArray(data['categories'])) throw new Error('content.data.categories must be array');
      if (!Array.isArray(data['series'])) throw new Error('content.data.series must be array');
      const catLen = (data['categories'] as unknown[]).length;
      for (const [i, s] of (data['series'] as unknown[]).entries()) {
        if (!s || typeof s !== 'object') throw new Error(`content.data.series[${i}] not object`);
        const sv = (s as Record<string, unknown>)['values'];
        if (!Array.isArray(sv)) throw new Error(`content.data.series[${i}].values must be array`);
        if (sv.length !== catLen) throw new Error(`content.data.series[${i}].values length (${sv.length}) != categories length (${catLen})`);
      }
      return;
    }
    case 'quote':
      requireString(c, 'quote', 'content');
      return;
    case 'closing':
      return;
    case 'custom': {
      requireArray(c, 'elements', 'content');
      for (const [i, el] of (c['elements'] as unknown[]).entries()) {
        if (!el || typeof el !== 'object') throw new Error(`elements[${i}] not object`);
        const e = el as Record<string, unknown>;
        if (typeof e['type'] !== 'string' || !VALID_CUSTOM_TYPES.includes(e['type']))
          throw new Error(`elements[${i}].type must be one of ${VALID_CUSTOM_TYPES.join(', ')}`);
        for (const k of ['x', 'y', 'w', 'h']) {
          if (typeof e[k] !== 'number') throw new Error(`elements[${i}].${k} (number) is required`);
        }
        if (e['type'] === 'image' && typeof e['path'] === 'string') {
          try { resolveAndGuard(ctx.workspacePath, e['path']); }
          catch (err) { throw new Error(`elements[${i}].path: ${(err as Error).message}`); }
        }
      }
      return;
    }
  }
}
