/**
 * CalendarImportAgent — A2A pipeline for ChronoMind calendar event import + conflict detection.
 *
 * Agent roster (3 steps):
 *   1. EventValidatorAgent    — validate supplied iCal event objects against ChronoMind timer schema
 *   2. ConflictDetectorAgent  — compare events against existing timers for time overlaps
 *   3. ImportReportAgent      — report valid events ready to import, conflicts, and skipped items
 *
 * MCP tools:
 *   chronomind.calendar.import(events, dryRun?) — validate + conflict-check a batch of calendar events
 */

import { randomUUID } from 'node:crypto';
import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import type { McpToolRequest } from '../tools/types.js';
import {
  chronomindTimersList,
  chronomindTimerCreate,
  type TimerDoc,
} from '../../lib/chronomind-client.js';
import { config } from '../../lib/config.js';

// ── Input schema for a single calendar event ──────────────────────────────────

const CalendarEventSchema = z.object({
  uid: z.string().optional(),
  summary: z.string().min(1),
  dtstart: z.string().datetime({ offset: true }),
  dtend: z.string().datetime({ offset: true }).optional(),
  durationMinutes: z.coerce.number().int().min(1).optional(),
  description: z.string().optional(),
  location: z.string().optional(),
  rrule: z.string().optional(),
});

type CalendarEvent = z.infer<typeof CalendarEventSchema>;

// ── Types ──────────────────────────────────────────────────────────────────────

type ValidationStatus = 'valid' | 'invalid';
type ConflictStatus = 'no_conflict' | 'conflict' | 'partial_overlap';

interface EventValidationResult {
  uid: string;
  summary: string;
  validationStatus: ValidationStatus;
  validationError?: string;
  targetTime: string;
  endTime: string | null;
  durationMinutes: number | null;
}

interface EventWithConflict extends EventValidationResult {
  conflictStatus: ConflictStatus;
  conflictingTimerIds: string[];
}

interface ImportedTimer {
  uid: string;
  timerId: string;
  summary: string;
}

export interface CalendarImportReport {
  runId: string;
  productId: 'chronomind';
  dryRun: boolean;
  totalEvents: number;
  validEvents: number;
  invalidEvents: number;
  conflictEvents: number;
  importedCount: number;
  skippedCount: number;
  perEvent: EventWithConflict[];
  importedTimers: ImportedTimer[];
  summary: string;
  generatedAt: string;
}

// ── Step 1: EventValidatorAgent ────────────────────────────────────────────────

function validateEvent(event: CalendarEvent, index: number): EventValidationResult {
  const uid = event.uid ?? `event-${index}`;

  let endTime: string | null = null;
  let durationMinutes: number | null = null;

  if (event.dtend) {
    endTime = event.dtend;
    durationMinutes = Math.round(
      (new Date(event.dtend).getTime() - new Date(event.dtstart).getTime()) / 60000
    );
  } else if (event.durationMinutes) {
    durationMinutes = event.durationMinutes;
    endTime = new Date(
      new Date(event.dtstart).getTime() + event.durationMinutes * 60000
    ).toISOString();
  }

  if (durationMinutes !== null && durationMinutes <= 0) {
    return {
      uid,
      summary: event.summary,
      validationStatus: 'invalid',
      validationError: 'Event end time is before or equal to start time.',
      targetTime: event.dtstart,
      endTime,
      durationMinutes,
    };
  }

  const eventDate = new Date(event.dtstart);
  if (isNaN(eventDate.getTime())) {
    return {
      uid,
      summary: event.summary,
      validationStatus: 'invalid',
      validationError: 'Invalid dtstart date format.',
      targetTime: event.dtstart,
      endTime,
      durationMinutes,
    };
  }

  return {
    uid,
    summary: event.summary,
    validationStatus: 'valid',
    targetTime: event.dtstart,
    endTime,
    durationMinutes,
  };
}

// ── Step 2: ConflictDetectorAgent ─────────────────────────────────────────────

function detectConflict(
  event: EventValidationResult,
  existingTimers: TimerDoc[]
): EventWithConflict {
  const eventStart = new Date(event.targetTime).getTime();
  const eventEnd = event.endTime ? new Date(event.endTime).getTime() : eventStart + 60000;

  const conflictingTimerIds: string[] = [];
  let conflictStatus: ConflictStatus = 'no_conflict';

  for (const timer of existingTimers) {
    const timerTarget = (timer as unknown as Record<string, unknown>)['targetTime'];
    if (!timerTarget || typeof timerTarget !== 'string') continue;

    const timerStart = new Date(timerTarget).getTime();
    const timerDuration =
      (((timer as unknown as Record<string, unknown>)['durationSeconds'] as number) ?? 0) * 1000;
    const timerEnd = timerStart + (timerDuration || 3600000);

    const overlaps = eventStart < timerEnd && eventEnd > timerStart;
    if (overlaps) {
      conflictingTimerIds.push(timer.id);
      const isExact = eventStart === timerStart && eventEnd === timerEnd;
      conflictStatus = isExact ? 'conflict' : 'partial_overlap';
    }
  }

  return { ...event, conflictStatus, conflictingTimerIds };
}

// ── Step 3: ImportReportAgent ─────────────────────────────────────────────────

async function importValidEvents(
  events: EventWithConflict[],
  originalEvents: CalendarEvent[],
  dryRun: boolean,
  opts: { token?: string; requestId?: string }
): Promise<ImportedTimer[]> {
  if (dryRun) return [];

  const importable = events.filter(
    e => e.validationStatus === 'valid' && e.conflictStatus === 'no_conflict'
  );

  const imported: ImportedTimer[] = [];
  for (const event of importable) {
    const orig = originalEvents.find(o => (o.uid ?? '') === event.uid);
    try {
      const timer = await chronomindTimerCreate(
        {
          id: randomUUID(),
          type: 'alarm',
          label: event.summary,
          targetTime: event.targetTime,
          urgency: 'standard',
          state: 'active',
          syncVersion: 1,
          category: orig?.location ?? undefined,
        },
        opts
      );
      imported.push({ uid: event.uid, timerId: timer.id, summary: event.summary });
    } catch {
      // best-effort — log failure but continue
    }
  }
  return imported;
}

// ── Pipeline runner ────────────────────────────────────────────────────────────

async function runCalendarImportPipeline(
  events: CalendarEvent[],
  dryRun: boolean,
  req: McpToolRequest
): Promise<CalendarImportReport> {
  const runId = randomUUID();
  const opts = { token: req.headers.authorization?.slice(7), requestId: req.id };

  req.log.info(
    { runId, stepId: 'validate', eventCount: events.length, dryRun },
    'EventValidatorAgent start'
  );
  const validationResults = events.map((e, i) => validateEvent(e, i));
  const validEvents = validationResults.filter(r => r.validationStatus === 'valid');
  const invalidEvents = validationResults.filter(r => r.validationStatus === 'invalid');
  req.log.info(
    { runId, stepId: 'validate', valid: validEvents.length, invalid: invalidEvents.length },
    'EventValidatorAgent done'
  );

  req.log.info(
    { runId, stepId: 'conflict', validCount: validEvents.length },
    'ConflictDetectorAgent start'
  );
  let existingTimers: TimerDoc[] = [];
  if (validEvents.length > 0) {
    try {
      const result = await chronomindTimersList({ limit: config.QUERY_MAX_LIMIT }, opts);
      existingTimers = result.items;
    } catch {
      // best-effort
    }
  }

  const eventsWithConflict: EventWithConflict[] = validationResults.map(result => {
    if (result.validationStatus === 'invalid') {
      return {
        ...result,
        conflictStatus: 'no_conflict' as ConflictStatus,
        conflictingTimerIds: [],
      };
    }
    return detectConflict(result, existingTimers);
  });

  const conflictCount = eventsWithConflict.filter(e => e.conflictStatus !== 'no_conflict').length;
  req.log.info({ runId, stepId: 'conflict', conflictCount }, 'ConflictDetectorAgent done');

  req.log.info({ runId, stepId: 'import', dryRun }, 'ImportReportAgent start');
  const importedTimers = await importValidEvents(eventsWithConflict, events, dryRun, opts);

  const skippedCount = conflictCount + invalidEvents.length;
  const summary = dryRun
    ? `DRY RUN: ${validEvents.length}/${events.length} events valid, ${conflictCount} conflict(s), ${invalidEvents.length} invalid. ${validEvents.length - conflictCount} would be imported.`
    : `Imported ${importedTimers.length}/${events.length} events as alarms. ${conflictCount} skipped (conflict), ${invalidEvents.length} invalid.`;

  req.log.info({ runId, stepId: 'import', summary }, 'ImportReportAgent done');

  return {
    runId,
    productId: 'chronomind',
    dryRun,
    totalEvents: events.length,
    validEvents: validEvents.length,
    invalidEvents: invalidEvents.length,
    conflictEvents: conflictCount,
    importedCount: importedTimers.length,
    skippedCount,
    perEvent: eventsWithConflict,
    importedTimers,
    summary,
    generatedAt: new Date().toISOString(),
  };
}

// ── MCP tool registration ─────────────────────────────────────────────────────

registerTool({
  name: 'chronomind.calendar.import',
  description:
    'A2A pipeline: validates a batch of calendar events (dtstart, dtend, summary), detects time conflicts with existing ChronoMind timers, and imports conflict-free events as alarm timers. Use dryRun=true to preview without creating timers. Returns per-event validation + conflict status plus a list of imported timer IDs. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    events: z
      .array(CalendarEventSchema)
      .min(1)
      .describe('List of calendar events to import (from .ics or calendar API)'),
    dryRun: z
      .boolean()
      .default(false)
      .describe('If true, validate and check conflicts only — do not create timers'),
  }),
  async execute(args, req) {
    return runCalendarImportPipeline(args.events, args.dryRun, req);
  },
});
