import { Hono } from 'hono';
import type { Context } from 'hono';
import { z } from 'zod';

import { calendarSendPayloadSchema } from '@/entities/calendarSendItem';
import { getICalStats, listCalendarsForDevice, listEventsByFilter } from '@/features/ical';
import { enqueueEventChange } from '@/features/ical/sendService';

const limitParam = z.coerce.number().int().positive().max(1000).default(200);
const deviceIdParam = z.string().min(1).optional();
const dateParam = z.string().optional();

// Auth follow-up: deviceId is currently passed as a query/body param. Once
// SSO session resolution lands, this surface should derive the target
// deviceId from the session and drop the explicit parameter.
const requiredDeviceId = z.object({ deviceId: z.string().min(1) });

const createEventSchema = calendarSendPayloadSchema.extend({
  deviceId: z.string().min(1),
});

const updateEventSchema = calendarSendPayloadSchema.extend({
  deviceId: z.string().min(1),
});

function errorResponse(c: Context, err: unknown, fallback: string) {
  if (err instanceof z.ZodError) {
    return c.json(
      {
        success: false,
        error: { code: 'validation_error', message: 'invalid request payload', issues: err.issues },
      },
      400,
    );
  }
  return c.json(
    {
      success: false,
      error: { code: 'enqueue_error', message: err instanceof Error ? err.message : fallback },
    },
    500,
  );
}

export const calendarMyRouter = new Hono()
  .get('/stats', async (c) => {
    return c.json({ success: true, data: await getICalStats() });
  })
  .get('/calendars', async (c) => {
    const deviceId = deviceIdParam.parse(c.req.query('deviceId'));
    if (!deviceId) return c.json({ success: true, data: { calendars: [] } });
    return c.json({
      success: true,
      data: { calendars: await listCalendarsForDevice(deviceId) },
    });
  })
  .get('/events', async (c) => {
    const deviceId = deviceIdParam.parse(c.req.query('deviceId'));
    const startAfter = dateParam.parse(c.req.query('startAfter'));
    const startBefore = dateParam.parse(c.req.query('startBefore'));
    const calendarId = z.string().optional().parse(c.req.query('calendarId'));
    const limit = limitParam.parse(c.req.query('limit') ?? '200');
    const offset = z.coerce
      .number()
      .int()
      .nonnegative()
      .default(0)
      .parse(c.req.query('offset') ?? '0');

    const events = await listEventsByFilter({
      deviceId,
      startAfter,
      startBefore,
      calendarId,
      limit,
      offset,
    });
    return c.json({ success: true, data: { events } });
  })
  .post('/events', async (c) => {
    try {
      const body = createEventSchema.parse(await c.req.json());
      const { deviceId, ...payload } = body;
      const { id } = await enqueueEventChange({ deviceId, action: 'create_event', payload });
      return c.json({ success: true, data: { sendQueueId: id } });
    } catch (err) {
      return errorResponse(c, err, 'failed to enqueue create_event');
    }
  })
  .put('/events/:id', async (c) => {
    try {
      const eventIdentifier = c.req.param('id');
      const body = updateEventSchema.parse(await c.req.json());
      const { deviceId, ...rest } = body;
      const payload = { ...rest, eventIdentifier };
      const { id } = await enqueueEventChange({ deviceId, action: 'update_event', payload });
      return c.json({ success: true, data: { sendQueueId: id } });
    } catch (err) {
      return errorResponse(c, err, 'failed to enqueue update_event');
    }
  })
  .delete('/events/:id', async (c) => {
    try {
      const eventIdentifier = c.req.param('id');
      const { deviceId } = requiredDeviceId.parse({ deviceId: c.req.query('deviceId') });
      const { id } = await enqueueEventChange({
        deviceId,
        action: 'delete_event',
        payload: { eventIdentifier },
      });
      return c.json({ success: true, data: { sendQueueId: id } });
    } catch (err) {
      return errorResponse(c, err, 'failed to enqueue delete_event');
    }
  });
