/**
 * ChronoMind MCP tools — chronomind.timers.*, chronomind.routines.*, chronomind.syncStatus
 *
 * Backed by: chronomind-backend (port 4011).
 * All tools require admin role.
 */

import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import { config } from '../../lib/config.js';
import {
  chronomindTimerCreate,
  chronomindTimersList,
  chronomindTimerDelete,
  chronomindTimerReschedule,
  chronomindTimerAvailability,
  chronomindRoutineGet,
  chronomindRoutinesList,
  chronomindRoutineStart,
  chronomindSyncStatus,
  chronomindHouseholdsList,
  chronomindSharedTimerShare,
  chronomindAgentActionCreate,
  chronomindAgentActionsList,
  chronomindAgentActionApprove,
  chronomindPlannerPlanDay,
  chronomindPlannerPlanNl,
} from '../../lib/chronomind-client.js';
import type { McpToolRequest } from '../tools/types.js';

const tokenOf = (req: McpToolRequest) => req.headers.authorization?.slice(7);

// ── chronomind.timers.create ───────────────────────────────────────────────

registerTool({
  name: 'chronomind.timers.create',
  description:
    'Create a new cloud-synced timer. The id and syncVersion must be provided by the caller (client-generated UUID and current version). Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    id: z.string().min(1).describe('Client-generated timer UUID'),
    label: z.string().min(1).describe('Timer display name'),
    type: z.enum(['countdown', 'alarm', 'pomodoro']),
    urgency: z.enum(['critical', 'important', 'standard', 'gentle', 'passive']).default('standard'),
    duration: z.coerce.number().optional().describe('Duration in seconds (countdown/pomodoro)'),
    targetTime: z.string().optional().describe('ISO 8601 target time (alarm/event)'),
    category: z.string().optional().describe('Timer category label'),
    syncVersion: z.coerce.number().default(1),
    deviceId: z.string().optional().describe('Origin device ID for conflict detection'),
  }),
  async execute(args, req) {
    return chronomindTimerCreate(
      { ...args, state: 'active' },
      { token: tokenOf(req), requestId: req.id }
    );
  },
});

// ── chronomind.timers.list ────────────────────────────────────────────────

registerTool({
  name: 'chronomind.timers.list',
  description:
    'List cloud-synced timers for the authenticated user. Filter by type (countdown/alarm/pomodoro) or state (active/paused/fired/snoozed/dismissed/completed/warning). Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    type: z.enum(['countdown', 'alarm', 'pomodoro']).optional().describe('Filter by timer type'),
    state: z
      .enum(['active', 'paused', 'fired', 'snoozed', 'dismissed', 'completed', 'warning'])
      .optional()
      .describe('Filter by timer state'),
    limit: z.coerce.number().min(1).max(config.QUERY_MAX_LIMIT).default(config.QUERY_DEFAULT_LIMIT),
    offset: z.coerce.number().min(0).default(0),
  }),
  async execute(args, req) {
    return chronomindTimersList(args, { token: tokenOf(req), requestId: req.id });
  },
});

// ── chronomind.timers.delete ──────────────────────────────────────────────

registerTool({
  name: 'chronomind.timers.delete',
  description: 'Delete a timer by ID. Use with care — deletion is permanent. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    timerId: z.string().min(1).describe('Timer ID to delete'),
  }),
  async execute(args, req) {
    return chronomindTimerDelete(args.timerId, { token: tokenOf(req), requestId: req.id });
  },
});

// ── chronomind.routines.list ──────────────────────────────────────────────

registerTool({
  name: 'chronomind.routines.list',
  description:
    'List cloud-synced routines for the authenticated user. Optionally filter to template routines only. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    isTemplate: z
      .boolean()
      .optional()
      .describe('true = only templates, false = only user routines'),
    limit: z.coerce.number().min(1).max(config.QUERY_MAX_LIMIT).default(config.QUERY_DEFAULT_LIMIT),
    offset: z.coerce.number().min(0).default(0),
  }),
  async execute(args, req) {
    return chronomindRoutinesList(args, { token: tokenOf(req), requestId: req.id });
  },
});

// ── chronomind.syncStatus ─────────────────────────────────────────────────

registerTool({
  name: 'chronomind.syncStatus',
  description:
    'Instant sync health check: total timers, active count, pending count, unsynced count, and last sync timestamp. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({}),
  async execute(_args, req) {
    return chronomindSyncStatus({ token: tokenOf(req), requestId: req.id });
  },
});

// ── chronomind.sharedTimers.share ──────────────────────────────────────────

registerTool({
  name: 'chronomind.sharedTimers.share',
  description:
    'Share a timer with one or more household members or specific user IDs. Creates a shared-timer record in the shared-timers module. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    timerId: z.string().min(1).describe('ID of the timer to share'),
    targets: z
      .array(
        z.object({
          userId: z.string().optional().describe('Target user ID'),
          householdId: z
            .string()
            .optional()
            .describe('Target household ID (shares with all members)'),
        })
      )
      .min(1)
      .describe('Recipients — provide userId or householdId for each target'),
  }),
  async execute(args, req) {
    return chronomindSharedTimerShare(args.timerId, args.targets, {
      token: tokenOf(req),
      requestId: req.id,
    });
  },
});

// ── chronomind.routines.validate ─────────────────────────────────────────

registerTool({
  name: 'chronomind.routines.validate',
  description:
    'Validate a routine for structural integrity: checks that total duration does not exceed the maximum, that all steps have non-zero duration, and that the routine has at least one step. Returns pass/fail with a list of issues. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    routineId: z.string().min(1).describe('Routine ID to validate'),
    maxDurationMinutes: z.coerce
      .number()
      .min(1)
      .max(1440)
      .default(480)
      .describe('Maximum allowed total duration in minutes (default 480 = 8h)'),
  }),
  async execute(args, req) {
    let routine;
    try {
      routine = await chronomindRoutineGet(args.routineId, {
        token: tokenOf(req),
        requestId: req.id,
      });
    } catch {
      return { valid: false, routineId: args.routineId, issues: ['Routine not found'] };
    }
    const issues: string[] = [];
    if (!routine.steps || routine.steps.length === 0) issues.push('Routine has no steps');
    if (routine.totalDurationMinutes > args.maxDurationMinutes) {
      issues.push(
        `Total duration ${routine.totalDurationMinutes}min exceeds max ${args.maxDurationMinutes}min`
      );
    }
    return {
      valid: issues.length === 0,
      routineId: routine.id,
      routineName: routine.name,
      stepCount: routine.steps.length,
      totalDurationMinutes: routine.totalDurationMinutes,
      maxDurationMinutes: args.maxDurationMinutes,
      issues,
    };
  },
});

// ── chronomind.households.list ──────────────────────────────────────────────

registerTool({
  name: 'chronomind.households.list',
  description:
    'List ChronoMind Family-tier households the authenticated user belongs to, including member list and invite codes. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    limit: z.coerce.number().min(1).max(config.QUERY_MAX_LIMIT).default(config.QUERY_DEFAULT_LIMIT),
    offset: z.coerce.number().min(0).default(0),
  }),
  async execute(args, req) {
    return chronomindHouseholdsList(args, { token: tokenOf(req), requestId: req.id });
  },
});

// ══════════════════════════════════════════════════════════════════════════════
// Phase A.3 — New MCP tools for agentic AI features
// ══════════════════════════════════════════════════════════════════════════════

// ── chronomind.timers.reschedule ────────────────────────────────────────────

registerTool({
  name: 'chronomind.timers.reschedule',
  description:
    'Reschedule a timer by shifting it forward/backward (deltaSeconds) or setting a new absolute target time. Provide exactly one of deltaSeconds or targetTime. Records an agent action for audit trail. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    timerId: z.string().min(1).describe('Timer ID to reschedule'),
    deltaSeconds: z
      .number()
      .int()
      .optional()
      .describe('Shift by N seconds (positive = later, negative = earlier)'),
    targetTime: z.string().optional().describe('New absolute target time (ISO 8601)'),
    syncVersion: z.coerce.number().min(1).describe('Current sync version for conflict check'),
    reason: z.string().optional().describe('Reason for rescheduling (recorded in audit trail)'),
  }),
  async execute(args, req) {
    const opts = { token: tokenOf(req), requestId: req.id };

    // Record agent action audit trail
    await chronomindAgentActionCreate(
      {
        id: `aa_${crypto.randomUUID()}`,
        actorId: 'mcp-server',
        actorType: 'mcp',
        toolName: 'chronomind.timers.reschedule',
        actionType: 'timer.reschedule',
        reason: args.reason,
        payload: {
          timerId: args.timerId,
          deltaSeconds: args.deltaSeconds,
          targetTime: args.targetTime,
        },
      },
      opts
    ).catch(() => {
      /* fail-open: audit failure should not block the action */
    });

    return chronomindTimerReschedule(
      args.timerId,
      {
        deltaSeconds: args.deltaSeconds,
        targetTime: args.targetTime,
        syncVersion: args.syncVersion,
      },
      opts
    );
  },
});

// ── chronomind.timers.availability ──────────────────────────────────────────

registerTool({
  name: 'chronomind.timers.availability',
  description:
    'Find free time slots within a given time window. Returns gaps between existing timers that are at least minSlotMinutes long. Useful for scheduling new timers without conflicts. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    start: z.string().describe('Start of time window (ISO 8601)'),
    end: z.string().describe('End of time window (ISO 8601)'),
    minSlotMinutes: z.coerce
      .number()
      .min(1)
      .max(480)
      .default(15)
      .describe('Minimum free slot duration in minutes'),
  }),
  async execute(args, req) {
    return chronomindTimerAvailability(args, { token: tokenOf(req), requestId: req.id });
  },
});

// ── chronomind.routines.start ───────────────────────────────────────────────

registerTool({
  name: 'chronomind.routines.start',
  description:
    'Start a routine that is in "ready" or "template" status. Transitions its status to "active" and sets the first step as active. Records an agent action for audit trail. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    routineId: z.string().min(1).describe('Routine ID to start'),
    reason: z.string().optional().describe('Reason for starting (recorded in audit trail)'),
  }),
  async execute(args, req) {
    const opts = { token: tokenOf(req), requestId: req.id };

    // Record agent action audit trail
    await chronomindAgentActionCreate(
      {
        id: `aa_${crypto.randomUUID()}`,
        actorId: 'mcp-server',
        actorType: 'mcp',
        toolName: 'chronomind.routines.start',
        actionType: 'routine.start',
        reason: args.reason,
        payload: { routineId: args.routineId },
      },
      opts
    ).catch(() => {
      /* fail-open: audit failure should not block the action */
    });

    return chronomindRoutineStart(args.routineId, opts);
  },
});

// ── chronomind.agentActions.list ─────────────────────────────────────────────

registerTool({
  name: 'chronomind.agentActions.list',
  description:
    'List agent action audit trail entries. Filter by state (proposed/approved/applied/rejected), actorId, or toolName. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    state: z
      .enum(['proposed', 'approved', 'applied', 'rejected'])
      .optional()
      .describe('Filter by action state'),
    actorId: z.string().optional().describe('Filter by actor ID'),
    toolName: z.string().optional().describe('Filter by MCP tool name'),
    limit: z.coerce.number().min(1).max(config.QUERY_MAX_LIMIT).default(config.QUERY_DEFAULT_LIMIT),
    offset: z.coerce.number().min(0).default(0),
  }),
  async execute(args, req) {
    return chronomindAgentActionsList(args, { token: tokenOf(req), requestId: req.id });
  },
});

// ── chronomind.agentActions.approve ──────────────────────────────────────────

registerTool({
  name: 'chronomind.agentActions.approve',
  description:
    'Approve a proposed agent action. Only actions in "proposed" state can be approved. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    actionId: z.string().min(1).describe('Agent action ID to approve'),
  }),
  async execute(args, req) {
    return chronomindAgentActionApprove(args.actionId, { token: tokenOf(req), requestId: req.id });
  },
});

// ══════════════════════════════════════════════════════════════════════════════
// Phase B.2b — Day Planner MCP tools
// ══════════════════════════════════════════════════════════════════════════════

// ── chronomind.planner.planDay ───────────────────────────────────────────────

registerTool({
  name: 'chronomind.planner.planDay',
  description:
    'Generate a proposed day plan from a list of activities. The engine computes free slots around existing timers, fits activities by priority, assigns urgency and cascade presets, and handles overflow. Returns proposed timers ready for review and apply. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    date: z.string().describe('Target date in YYYY-MM-DD format'),
    dayStartHour: z.coerce.number().min(0).max(23).default(8).describe('Day starts at this hour'),
    dayEndHour: z.coerce.number().min(1).max(24).default(22).describe('Day ends at this hour'),
    activities: z
      .array(
        z.object({
          label: z.string().min(1).describe('Activity name'),
          durationMinutes: z.coerce.number().min(1).max(480).describe('Duration in minutes'),
          priority: z.enum(['high', 'medium', 'low']).default('medium'),
          category: z.string().optional().describe('Activity category'),
        })
      )
      .min(1)
      .max(20)
      .describe('Activities to schedule'),
    prepTimeMinutes: z.coerce
      .number()
      .min(0)
      .max(30)
      .default(5)
      .describe('Buffer between activities'),
  }),
  async execute(args, req) {
    const opts = { token: tokenOf(req), requestId: req.id };

    await chronomindAgentActionCreate(
      {
        id: `aa_${crypto.randomUUID()}`,
        actorId: 'mcp-server',
        actorType: 'mcp',
        toolName: 'chronomind.planner.planDay',
        actionType: 'planner.plan_day',
        payload: { date: args.date, activityCount: args.activities.length },
      },
      opts
    ).catch(() => {});

    return chronomindPlannerPlanDay(args, opts);
  },
});

// ── chronomind.planner.planNl ────────────────────────────────────────────────

registerTool({
  name: 'chronomind.planner.planNl',
  description:
    'Parse natural language text describing a day plan into structured activities, then generate a proposed day plan. Supports semicolon/newline/then separators, durations (30m, 1h, half hour), priority keywords, and time constraints (after 2pm, before 10am). Returns parsed activities, any unparsed segments, and the proposed timeline. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    text: z
      .string()
      .min(1)
      .max(2000)
      .describe(
        'Natural language description of activities, e.g. "Meeting 30m; Deep work 2h; Gym 1h after 2pm"'
      ),
    date: z.string().optional().describe('Target date in YYYY-MM-DD format (defaults to today)'),
    dayStartHour: z.coerce.number().min(0).max(23).default(8).describe('Day starts at this hour'),
    dayEndHour: z.coerce.number().min(1).max(24).default(22).describe('Day ends at this hour'),
    prepTimeMinutes: z.coerce
      .number()
      .min(0)
      .max(30)
      .default(5)
      .describe('Buffer between activities'),
  }),
  async execute(args, req) {
    const opts = { token: tokenOf(req), requestId: req.id };

    await chronomindAgentActionCreate(
      {
        id: `aa_${crypto.randomUUID()}`,
        actorId: 'mcp-server',
        actorType: 'mcp',
        toolName: 'chronomind.planner.planNl',
        actionType: 'planner.plan_nl',
        payload: { textLength: args.text.length },
      },
      opts
    ).catch(() => {});

    return chronomindPlannerPlanNl(args, opts);
  },
});
