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

import {
  queryConversations,
  queryMessages,
  queryMessagesByHandle,
  queryMessageSearch,
  queryInboundSince,
  queryActiveConversations,
  getLastSync,
} from '@/features/imessage';
import type { SearchMode } from '@/features/imessage';

const byHandleLimitParam = z.coerce.number().int().positive().max(500).default(50);
const handleParam = z.string().min(1);

const limitParam = z.coerce.number().int().positive().max(500).default(100);
const sinceParam = z.string().optional();
const convIdParam = z.string().min(1);

const searchLimitParam = z.coerce.number().int().positive().default(50);
const searchOffsetParam = z.coerce.number().int().min(0).default(0);
const searchSinceParam = z.string().optional();
const searchQueryParam = z.string().trim().min(1).max(256);
const searchModeParam = z.enum(['lexical', 'semantic', 'hybrid']).default('hybrid');
const optionalConvIdParam = z.string().min(1).optional();
const optionalDeviceIdParam = z.string().min(1).optional();

export const messagesMyRouter = new Hono()
  .get('/by-handle', async (c) => {
    const handleResult = handleParam.safeParse(c.req.query('handle') ?? '');
    if (!handleResult.success) {
      return c.json({ error: 'query parameter `handle` is required (min 1 char)' }, 400);
    }
    const handle = handleResult.data;
    const deviceId = optionalDeviceIdParam.parse(c.req.query('deviceId'));
    const since = sinceParam.parse(c.req.query('since'));
    const limit = byHandleLimitParam.parse(c.req.query('limit') ?? '50');
    const result = await queryMessagesByHandle({ handle, deviceId, since, limit });
    return c.json({ handle, count: result.count, messages: result.messages });
  })
  .get('/conversations', async (c) => {
    const deviceId = c.req.query('deviceId') ?? undefined;
    return c.json(await queryConversations(deviceId));
  })
  .get('/conversations/:id/messages', async (c) => {
    const convId = convIdParam.parse(c.req.param('id'));
    const limit = limitParam.parse(c.req.query('limit') ?? '100');
    const since = sinceParam.parse(c.req.query('since'));
    return c.json(await queryMessages({ conversationId: convId, limit, since }));
  })
  .get('/search', async (c) => {
    const qParsed = searchQueryParam.safeParse(c.req.query('q') ?? '');
    if (!qParsed.success) {
      return c.json(
        { error: 'query parameter `q` is required (1–256 chars after trim)' },
        400,
      );
    }
    const query = qParsed.data;
    const limit = searchLimitParam.parse(c.req.query('limit') ?? '50');
    const offset = searchOffsetParam.parse(c.req.query('offset') ?? '0');
    const since = searchSinceParam.parse(c.req.query('since'));
    const modeParsed = searchModeParam.safeParse(c.req.query('mode') ?? 'hybrid');
    const mode: SearchMode = modeParsed.success ? modeParsed.data : 'hybrid';
    const conversationId = optionalConvIdParam.parse(c.req.query('conversationId'));
    const deviceId = optionalDeviceIdParam.parse(c.req.query('deviceId'));
    let result: Awaited<ReturnType<typeof queryMessageSearch>>;
    try {
      result = await queryMessageSearch({ query, mode, conversationId, deviceId, limit, offset, since });
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      if (msg.includes('embedQuery') || msg.includes('llama-http-embed') || msg.includes('unreachable')) {
        return c.json({ error: `Embedding service unavailable: ${msg}` }, 503);
      }
      throw err;
    }
    return c.json({
      query,
      mode,
      limit,
      offset,
      total: result.total,
      searchDate: new Date().toISOString(),
      results: result.hits,
    });
  })
  .get('/inbound', async (c) => {
    const sinceParsed = z.string().min(1).safeParse(c.req.query('since'));
    if (!sinceParsed.success) {
      return c.json({ error: 'query parameter `since` is required (ISO 8601)' }, 400);
    }
    const since = sinceParsed.data;
    if (Number.isNaN(Date.parse(since))) {
      return c.json({ error: `since is not valid ISO 8601: ${since}` }, 400);
    }
    const limit = z.coerce.number().int().positive().max(200).default(50)
      .parse(c.req.query('limit') ?? '50');
    const rows = await queryInboundSince({ sinceIso: since, limit });
    const messages = rows.map((m) => ({
      id: m.id,
      from_handle: m.fromHandle,
      conversation_id: m.conversationId,
      body: m.body,
      sent_at: m.sentAt,
    }));
    return c.json({ count: messages.length, messages });
  })
  .get('/conversations/active', async (c) => {
    const hours = z.coerce.number().int().positive().max(24 * 30).default(24)
      .parse(c.req.query('hours') ?? '24');
    const rows = await queryActiveConversations(hours);
    const conversations = rows.map((r) => ({
      conversation_id: r.conversationId,
      handle: r.handle,
      last_inbound_at: r.lastInboundAt,
      inbound_count: r.inboundCount,
    }));
    return c.json({ hours, count: conversations.length, conversations });
  })
  .get('/last-sync', async (c) => {
    const deviceId = c.req.query('deviceId') ?? '';
    if (!deviceId) return c.json({ lastSync: null });
    return c.json({ lastSync: await getLastSync(deviceId) });
  });
