import * as fs from 'fs';
import * as path from 'path';
import { ToolDef } from '../../llm/openai-compat.js';
import type { ToolContext, ToolResult } from './core.js';
import { resolveAndGuard } from './core.js';

// --- Name validation ---
const NAME_REGEX = /^[a-z0-9][a-z0-9-]{0,62}$/;

// --- Checklist JSON structure ---
interface ChecklistItem {
  id: string;
  label: string;
  status: 'pending' | 'done' | 'failed' | 'skipped';
  result: string | null;
  error: string | null;
  checked_at: string | null;
}

interface ChecklistSummary {
  total: number;
  done: number;
  failed: number;
  skipped: number;
  remaining: number;
}

interface ChecklistData {
  name: string;
  created_at: string;
  updated_at: string;
  items: ChecklistItem[];
  summary: ChecklistSummary;
}

// --- Tool definitions ---

const CREATE_CHECKLIST_DEF: ToolDef = {
  type: 'function',
  function: {
    name: 'CreateChecklist',
    description: '複数アイテム処理のためのチェックリストを作成する。workspace/logs/checklists/{name}.json に保存。「1件処理→即CheckItem」のループで使う。詳細は ReadToolDoc({ name: "CreateChecklist" }) で取得可能。',
    parameters: {
      type: 'object',
      properties: {
        name: { type: 'string', description: 'チェックリスト名（小文字英数字とハイフンのみ、1-63文字）' },
        items: {
          type: 'array',
          description: 'チェック項目の配列',
          items: {
            type: 'object',
            properties: {
              id: { type: 'string', description: '項目ID' },
              label: { type: 'string', description: '項目ラベル' },
            },
            required: ['id', 'label'],
          },
        },
      },
      required: ['name', 'items'],
    },
  },
};

// NOTE: CheckItem must NOT be in PARALLEL_SAFE_TOOL_NAMES
// (it mutates shared checklist state and concurrent writes would cause data loss)
const CHECK_ITEM_DEF: ToolDef = {
  type: 'function',
  function: {
    name: 'CheckItem',
    description: 'チェックリストの項目をチェックする（done/failed/skipped）。1件処理した直後に呼ぶこと（まとめ呼び出し禁止）。詳細は ReadToolDoc({ name: "CheckItem" })。',
    parameters: {
      type: 'object',
      properties: {
        name: { type: 'string', description: 'チェックリスト名' },
        item_id: { type: 'string', description: '項目ID' },
        status: { type: 'string', enum: ['done', 'failed', 'skipped'], description: 'ステータス（デフォルト: done）' },
        result: { type: 'string', description: '結果メモ（任意）' },
        error: { type: 'string', description: 'エラー内容（任意）' },
      },
      required: ['name', 'item_id'],
    },
  },
};

const GET_CHECKLIST_DEF: ToolDef = {
  type: 'function',
  function: {
    name: 'GetChecklist',
    description: 'チェックリストの現在の状態を JSON で返す。',
    parameters: {
      type: 'object',
      properties: {
        name: { type: 'string', description: 'チェックリスト名' },
      },
      required: ['name'],
    },
  },
};

export const TOOL_DEFS: Record<string, ToolDef> = {
  CreateChecklist: CREATE_CHECKLIST_DEF,
  CheckItem: CHECK_ITEM_DEF,
  GetChecklist: GET_CHECKLIST_DEF,
};

// --- Helpers ---

function checklistPath(workspacePath: string, name: string): string {
  return resolveAndGuard(workspacePath, `logs/checklists/${name}.json`);
}

function computeSummary(items: ChecklistItem[]): ChecklistSummary {
  let done = 0;
  let failed = 0;
  let skipped = 0;
  for (const item of items) {
    if (item.status === 'done') done++;
    else if (item.status === 'failed') failed++;
    else if (item.status === 'skipped') skipped++;
  }
  return {
    total: items.length,
    done,
    failed,
    skipped,
    remaining: items.length - done - failed - skipped,
  };
}

function readChecklist(filePath: string): ChecklistData {
  const raw = fs.readFileSync(filePath, 'utf-8');
  return JSON.parse(raw) as ChecklistData;
}

function writeChecklist(filePath: string, data: ChecklistData): void {
  // Encapsulate mutation: update timestamp and recompute summary
  data.updated_at = new Date().toISOString();
  data.summary = computeSummary(data.items);
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
}

// --- Tool implementations ---

function executeCreateChecklist(input: Record<string, unknown>, ctx: ToolContext): ToolResult {
  const name = input.name as string | undefined;
  if (!name) {
    return { output: 'name is required', isError: true };
  }

  if (!NAME_REGEX.test(name)) {
    return { output: `Invalid checklist name: "${name}". Must match /^[a-z0-9][a-z0-9-]{0,62}$/`, isError: true };
  }

  const items = input.items as Array<{ id: string; label: string }> | undefined;
  if (!items || items.length === 0) {
    return { output: 'items must be a non-empty array', isError: true };
  }

  let filePath: string;
  try {
    filePath = checklistPath(ctx.workspacePath, name);
  } catch (e) {
    return { output: (e as Error).message, isError: true };
  }

  if (fs.existsSync(filePath)) {
    return { output: `Checklist "${name}" already exists`, isError: true };
  }

  const now = new Date().toISOString();
  const checklistItems: ChecklistItem[] = items.map((item) => ({
    id: item.id,
    label: item.label,
    status: 'pending',
    result: null,
    error: null,
    checked_at: null,
  }));

  const data: ChecklistData = {
    name,
    created_at: now,
    updated_at: now, // writeChecklist will update this, but we set it here for initial creation
    items: checklistItems,
    summary: computeSummary(checklistItems), // writeChecklist will recompute this
  };

  writeChecklist(filePath, data);
  return { output: `Checklist "${name}" created with ${items.length} items`, isError: false };
}

function executeCheckItem(input: Record<string, unknown>, ctx: ToolContext): ToolResult {
  const name = input.name as string | undefined;
  const itemId = input.item_id as string | undefined;
  if (!name) return { output: 'name is required', isError: true };
  if (!itemId) return { output: 'item_id is required', isError: true };

  if (!NAME_REGEX.test(name)) {
    return { output: `Invalid checklist name: "${name}". Must match /^[a-z0-9][a-z0-9-]{0,62}$/`, isError: true };
  }

  let filePath: string;
  try {
    filePath = checklistPath(ctx.workspacePath, name);
  } catch (e) {
    return { output: (e as Error).message, isError: true };
  }

  if (!fs.existsSync(filePath)) {
    return { output: `Checklist "${name}" not found`, isError: true };
  }

  const data = readChecklist(filePath);
  const item = data.items.find((i) => i.id === itemId);
  if (!item) {
    return { output: `Item "${itemId}" not found in checklist "${name}"`, isError: true };
  }

  const status = (input.status as string | undefined) ?? 'done';
  item.status = status as ChecklistItem['status'];
  item.result = (input.result as string | undefined) ?? null;
  item.error = (input.error as string | undefined) ?? null;
  item.checked_at = new Date().toISOString();

  writeChecklist(filePath, data);
  return { output: `Item "${itemId}" marked as ${status}`, isError: false };
}

function executeGetChecklist(input: Record<string, unknown>, ctx: ToolContext): ToolResult {
  const name = input.name as string | undefined;
  if (!name) return { output: 'name is required', isError: true };

  if (!NAME_REGEX.test(name)) {
    return { output: `Invalid checklist name: "${name}". Must match /^[a-z0-9][a-z0-9-]{0,62}$/`, isError: true };
  }

  let filePath: string;
  try {
    filePath = checklistPath(ctx.workspacePath, name);
  } catch (e) {
    return { output: (e as Error).message, isError: true };
  }

  if (!fs.existsSync(filePath)) {
    return { output: `Checklist "${name}" not found`, isError: true };
  }

  const data = readChecklist(filePath);
  return { output: JSON.stringify(data, null, 2), isError: false };
}

// --- Dispatcher ---

export async function executeTool(
  name: string,
  input: Record<string, unknown>,
  ctx: ToolContext,
): Promise<ToolResult | null> {
  switch (name) {
    case 'CreateChecklist':
      return executeCreateChecklist(input, ctx);
    case 'CheckItem':
      return executeCheckItem(input, ctx);
    case 'GetChecklist':
      return executeGetChecklist(input, ctx);
    default:
      return null;
  }
}
