/**
 * GET  /api/schedules    list with decorated state fields
 * POST /api/schedules    create
 *
 * V2: body is described by body_kind + body_ref instead of pipeline_name.
 * Phase 1 only accepts body_kind='pipeline' (and defaults to it if
 * omitted) so existing clients posting pipeline_name still work via a
 * compat alias. skill / connector_tool bodies land in later phases.
 */

import { NextResponse } from 'next/server';
import {
  listSchedules,
  createSchedule,
  reconcileStaleScheduleRuns,
} from '@/lib/schedules/store';
import { decorateSchedule } from '@/lib/schedules/state';
import { listWorkflows } from '@/lib/pipeline';
import { getProjectInfo, SCRATCH_PROJECT_NAME } from '@/lib/projects';
import { getPrompt } from '@/lib/prompts/store';
import type {
  ScheduleKind,
  ScheduleBodyKind,
  ScheduleActionKind,
  CreateScheduleInput,
} from '@/lib/schedules/types';

export async function GET() {
  // Reconcile zombies before reading so UI sees correct inflight counts.
  reconcileStaleScheduleRuns();
  const schedules = listSchedules().map(decorateSchedule);
  return NextResponse.json({ schedules });
}

const VALID_KINDS: ScheduleKind[] = ['period', 'once', 'cron', 'manual'];
const VALID_BODY_KINDS: ScheduleBodyKind[] = ['pipeline', 'prompt'];
const VALID_ACTION_KINDS: ScheduleActionKind[] = ['none', 'chat', 'email', 'telegram'];

export async function POST(req: Request) {
  let body: any = {};
  try { body = await req.json(); } catch {}

  // Required
  if (!body?.name || typeof body.name !== 'string') {
    return NextResponse.json({ error: 'name is required' }, { status: 400 });
  }

  // Compat: accept body_ref OR legacy pipeline_name (with body_kind defaulting to 'pipeline').
  const bodyRef: string | undefined = body.body_ref || body.pipeline_name;
  const bodyKind: ScheduleBodyKind = VALID_BODY_KINDS.includes(body.body_kind)
    ? body.body_kind : 'pipeline';

  if (!bodyRef || typeof bodyRef !== 'string') {
    return NextResponse.json({ error: 'body_ref is required' }, { status: 400 });
  }
  const input = (body.input && typeof body.input === 'object' && !Array.isArray(body.input))
    ? body.input as Record<string, unknown> : {};

  if (bodyKind === 'pipeline') {
    // Pipeline must exist + input shape check
    const workflow = listWorkflows().find((w) => w.name === bodyRef);
    if (!workflow) {
      return NextResponse.json({ error: `pipeline "${bodyRef}" not found` }, { status: 400 });
    }
    const declaredKeys = new Set(Object.keys(workflow.input || {}));
    for (const k of Object.keys(input)) {
      if (!declaredKeys.has(k)) {
        return NextResponse.json({
          error: `input field "${k}" is not declared by pipeline "${bodyRef}". Allowed: ${Array.from(declaredKeys).join(', ')}`,
        }, { status: 400 });
      }
    }
  } else if (bodyKind === 'prompt') {
    // Prompt definition must exist on disk
    if (!getPrompt(bodyRef)) {
      return NextResponse.json({
        error: `prompt definition not found: prompts/${bodyRef}.yaml — create it via POST /api/prompts first`,
      }, { status: 400 });
    }
    // input.project is OPTIONAL for prompt body — empty falls back to the
    // synthetic 'scratch' project that init.ts materializes under <dataDir>/scratch.
    // If specified, must resolve to a known project (scratch counts).
    const projectName = typeof input.project === 'string' ? input.project.trim() : '';
    if (!projectName) {
      input.project = SCRATCH_PROJECT_NAME;
    } else if (!getProjectInfo(projectName)) {
      return NextResponse.json({
        error: `project "${projectName}" not found in Forge's projects list`,
      }, { status: 400 });
    }
  }

  // Action — phase 4 ships chat. email/telegram land in phases 5-6.
  const actionKind: ScheduleActionKind = VALID_ACTION_KINDS.includes(body.action_kind)
    ? body.action_kind : 'none';
  const actionConfig = (body.action_config && typeof body.action_config === 'object' && !Array.isArray(body.action_config))
    ? body.action_config as Record<string, unknown> : {};
  if (actionKind === 'chat') {
    const sessionId = typeof actionConfig.session_id === 'string' ? actionConfig.session_id.trim() : '';
    if (!sessionId) {
      return NextResponse.json({
        error: 'action_kind=chat requires action_config.session_id',
      }, { status: 400 });
    }
  }
  if (actionKind === 'email') {
    const toRaw = actionConfig.to;
    const to = Array.isArray(toRaw)
      ? toRaw.filter((x) => typeof x === 'string' && x.trim())
      : (typeof toRaw === 'string' && toRaw.trim() ? [toRaw] : []);
    if (to.length === 0) {
      return NextResponse.json({
        error: 'action_kind=email requires action_config.to (recipient address or array)',
      }, { status: 400 });
    }
  }

  // Schedule kind + consistency
  const kind: ScheduleKind = VALID_KINDS.includes(body.schedule_kind) ? body.schedule_kind : 'period';
  if (kind === 'cron' && !body.schedule_cron) {
    return NextResponse.json({ error: 'schedule_cron is required when schedule_kind=cron' }, { status: 400 });
  }
  if (kind === 'once' && !body.schedule_at) {
    return NextResponse.json({ error: 'schedule_at is required when schedule_kind=once' }, { status: 400 });
  }

  // Skills (optional). Array of installed skill names; forwarded as
  // --append-system-prompt to the dispatched body's task(s). Ignored for
  // body_kind=connector_tool (no Claude task there).
  const skills: string[] = Array.isArray(body.skills)
    ? body.skills.filter((s: unknown) => typeof s === 'string' && s.trim()).map((s: string) => s.trim())
    : [];

  const input2: CreateScheduleInput = {
    name: body.name.trim(),
    body_kind: bodyKind,
    body_ref: bodyRef,
    input,
    skills,
    action_kind: actionKind,
    action_config: actionConfig,
    action_skip_on_empty: !!body.action_skip_on_empty,
    enabled: body.enabled !== false,
    schedule_kind: kind,
    schedule_interval_minutes: Number.isFinite(Number(body.schedule_interval_minutes))
      ? Math.max(1, Number(body.schedule_interval_minutes)) : 30,
    schedule_at: body.schedule_at || null,
    schedule_cron: body.schedule_cron || null,
  };
  const created = createSchedule(input2);
  return NextResponse.json({ schedule: decorateSchedule(created) });
}
