import type { ToolDef } from '../../llm/openai-compat.js';
import type { ToolContext, ToolResult } from './core.js';
import type { Repository } from '../../db/repository.js';

const DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
const TIME_PATTERN = /^([01]\d|2[0-3]):[0-5]\d$/;
const MAX_TITLE_LEN = 200;
const MAX_DESC_LEN = 4000;

let _repo: Repository | null = null;

export function setCalendarRepo(repo: Repository | null): void {
  _repo = repo;
}

const ADD_CALENDAR_EVENT_DEF: ToolDef = {
  type: 'function',
  function: {
    name: 'AddCalendarEvent',
    description:
      '実行中タスクのスペースのカレンダーに予定を1件追加する（created_by=agent）。詳細は ReadToolDoc({ name: "AddCalendarEvent" }) で取得可能。',
    parameters: {
      type: 'object',
      properties: {
        title: { type: 'string', description: '予定のタイトル（必須）' },
        date: { type: 'string', description: '開始日 YYYY-MM-DD（ローカル日付、必須）' },
        end_date: { type: 'string', description: '終了日 YYYY-MM-DD。複数日にまたがる予定のとき指定。省略すると単日' },
        time: { type: 'string', description: '開始 HH:MM。省略すると終日扱い' },
        end_time: { type: 'string', description: '終了 HH:MM。time（開始）があるときのみ有効。単日は開始以降のみ' },
        description: { type: 'string', description: '予定の補足説明（任意）' },
      },
      required: ['title', 'date'],
    },
  },
};

const LIST_CALENDAR_EVENTS_DEF: ToolDef = {
  type: 'function',
  function: {
    name: 'ListCalendarEvents',
    description:
      '実行中タスクのスペースのカレンダー予定を日付範囲で一覧する。詳細は ReadToolDoc({ name: "AddCalendarEvent" }) で取得可能。',
    parameters: {
      type: 'object',
      properties: {
        from: { type: 'string', description: '範囲開始 YYYY-MM-DD（任意）' },
        to: { type: 'string', description: '範囲終了 YYYY-MM-DD（任意）' },
      },
      required: [],
    },
  },
};

export const TOOL_DEFS: Record<string, ToolDef> = {
  AddCalendarEvent: ADD_CALENDAR_EVENT_DEF,
  ListCalendarEvents: LIST_CALENDAR_EVENTS_DEF,
};

export async function executeTool(
  name: string,
  input: Record<string, unknown>,
  ctx: ToolContext,
): Promise<ToolResult | null> {
  if (name === 'AddCalendarEvent') return executeAddCalendarEvent(input, ctx);
  if (name === 'ListCalendarEvents') return executeListCalendarEvents(input, ctx);
  return null;
}

async function executeAddCalendarEvent(
  input: Record<string, unknown>,
  ctx: ToolContext,
): Promise<ToolResult> {
  if (!_repo) {
    return { output: 'Calendar repo is not initialized', isError: true };
  }
  const spaceId = ctx.spaceId;
  if (!spaceId) {
    return {
      output:
        'このタスクはスペースに属していないため予定を追加できません（旧 local タスクには spaceId がありません）',
      isError: true,
    };
  }
  const title = input['title'];
  const date = input['date'];
  const endDateInput = input['end_date'];
  const time = input['time'];
  const endTimeInput = input['end_time'];
  const description = input['description'];

  if (typeof title !== 'string' || !title.trim()) {
    return { output: 'title は必須です', isError: true };
  }
  if (title.length > MAX_TITLE_LEN) {
    return { output: `title が長すぎます（最大 ${MAX_TITLE_LEN} 文字）`, isError: true };
  }
  if (typeof date !== 'string' || !DATE_PATTERN.test(date)) {
    return { output: 'date は YYYY-MM-DD 形式で指定してください', isError: true };
  }
  let endDate: string | null = null;
  if (endDateInput !== undefined && endDateInput !== null && endDateInput !== '') {
    if (typeof endDateInput !== 'string' || !DATE_PATTERN.test(endDateInput)) {
      return { output: 'end_date は YYYY-MM-DD 形式で指定してください', isError: true };
    }
    if (endDateInput < date) {
      return { output: 'end_date は date 以降の日付にしてください', isError: true };
    }
    endDate = endDateInput;
  }
  const startTime = typeof time === 'string' && time !== '' ? time : null;
  if (time !== undefined && time !== null && time !== '') {
    if (typeof time !== 'string' || !TIME_PATTERN.test(time)) {
      return { output: 'time は HH:MM 形式で指定してください', isError: true };
    }
  }
  let endTime: string | null = null;
  if (endTimeInput !== undefined && endTimeInput !== null && endTimeInput !== '') {
    if (typeof endTimeInput !== 'string' || !TIME_PATTERN.test(endTimeInput)) {
      return { output: 'end_time は HH:MM 形式で指定してください', isError: true };
    }
    if (startTime == null) {
      return { output: 'end_time は time（開始時刻）とあわせて指定してください', isError: true };
    }
    const multiDay = endDate != null && endDate > date;
    if (!multiDay && endTimeInput < startTime) {
      return { output: 'end_time は開始時刻以降にしてください（単日の予定）', isError: true };
    }
    endTime = endTimeInput;
  }
  if (description !== undefined && description !== null) {
    if (typeof description !== 'string') {
      return { output: 'description は文字列で指定してください', isError: true };
    }
    if (description.length > MAX_DESC_LEN) {
      return { output: `description が長すぎます（最大 ${MAX_DESC_LEN} 文字）`, isError: true };
    }
  }

  const sourceTaskId = ctx.taskId ? Number(ctx.taskId) : null;
  const ev = await _repo.createCalendarEvent({
    spaceId,
    ownerId: ctx.ownerId ?? null,
    date,
    endDate,
    time: startTime,
    endTime,
    title: title.trim(),
    description: typeof description === 'string' ? description : null,
    createdBy: 'agent',
    sourceTaskId: Number.isFinite(sourceTaskId) ? sourceTaskId : null,
  });
  const timeLabel = ev.time ? (ev.endTime ? `${ev.time}–${ev.endTime}` : ev.time) : '（終日）';
  return {
    output: `予定を追加しました: ${ev.date}${ev.endDate ? `–${ev.endDate}` : ''} ${timeLabel} 「${ev.title}」(id=${ev.id})`,
    isError: false,
  };
}

async function executeListCalendarEvents(
  input: Record<string, unknown>,
  ctx: ToolContext,
): Promise<ToolResult> {
  if (!_repo) {
    return { output: 'Calendar repo is not initialized', isError: true };
  }
  const spaceId = ctx.spaceId;
  if (!spaceId) {
    return {
      output: 'このタスクはスペースに属していないため予定を一覧できません',
      isError: true,
    };
  }
  const from = input['from'];
  const to = input['to'];
  if (from !== undefined && (typeof from !== 'string' || !DATE_PATTERN.test(from))) {
    return { output: 'from は YYYY-MM-DD 形式で指定してください', isError: true };
  }
  if (to !== undefined && (typeof to !== 'string' || !DATE_PATTERN.test(to))) {
    return { output: 'to は YYYY-MM-DD 形式で指定してください', isError: true };
  }
  const events = await _repo.listCalendarEvents(spaceId, {
    from: typeof from === 'string' ? from : undefined,
    to: typeof to === 'string' ? to : undefined,
  });
  if (events.length === 0) {
    return { output: '指定範囲に予定はありません', isError: false };
  }
  const lines = events.map((e) => {
    const timeLabel = e.time ? (e.endTime ? `${e.time}–${e.endTime}` : e.time) : '（終日）';
    return `- ${e.date}${e.endDate ? `–${e.endDate}` : ''} ${timeLabel} 「${e.title}」(id=${e.id}, ${e.createdBy})`;
  });
  return { output: lines.join('\n'), isError: false };
}
