/**
 * Internal routes — called by the Baileys WhatsApp worker.
 * These are NOT exposed via Kong / public gateway.
 * Restrict access by ensuring this route is only bound on a private interface
 * or protected by an internal secret.
 */
import type { FastifyInstance } from 'fastify';
import { contactService } from '../services/contactService.js';
import { ticketService } from '../services/ticketService.js';
import { messagingService } from '../services/messagingService.js';
import { channelService } from '../services/channelService.js';
import { auditService } from '../services/auditService.js';
import { talkbackService } from '../services/talkbackService.js';
import { notifyInboundAlerts } from '../services/channelAlertService.js';

export async function internalRoutes(fastify: FastifyInstance) {
  // POST /internal/messages  — called by Baileys worker on each inbound message
  fastify.post('/messages', async (req, reply) => {
    const { channelId, phone, waId, text } = req.body as {
      channelId: string;
      phone: string;
      waId: string;
      text: string;
    };

    if (!channelId || !phone || !text) {
      return reply.status(400).send({ error: 'channelId, phone, and text are required' });
    }

    // Deduplication check
    if (waId && await messagingService.isDuplicate(waId)) {
      return reply.status(200).send({ ok: true, duplicate: true });
    }

    // Ensure channel exists
    const channel = await channelService.getById(channelId);
    if (!channel) return reply.status(404).send({ error: 'Channel not found' });

    // Find or create contact
    const contact = await contactService.findOrCreate(phone);

    const priorCount = await ticketService.countForContactChannel(contact.id, channelId);

    // Find open ticket or create one (intake seeded for first-ever contact+channel if onboarding on)
    let ticket = await ticketService.findOpenTicket(contact.id, channelId);
    let isNew = false;

    if (!ticket) {
      const intake = ticketService.buildInitialIntake(channel.settings!, priorCount === 0);
      ticket = await ticketService.createTicket(contact.id, channelId, intake);
      isNew = true;
    }

    // Append message
    const message = await messagingService.appendMessage(ticket.id, 'inbound', text, waId ?? null);

    await auditService.log(null, 'system', 'message.received', 'message', message.id, {
      channelId,
      ticketId: ticket.id,
      phone,
    });

    // Emit real-time events
    if (isNew) {
      fastify.io.emit('ticket:created', { ticket });
    } else {
      fastify.io.to(`ticket:${ticket.id}`).emit('message:new', { ticketId: ticket.id, message });
    }

    if (channel.settings) {
      notifyInboundAlerts({
        ticketId: ticket.id,
        channelId,
        channelName: channel.name,
        fromLabel: phone,
        preview: text,
        settings: channel.settings,
      })
        .then(async (res: { flaggedEscalation: boolean }) => {
          if (res.flaggedEscalation) {
            const t2 = await ticketService.getById(ticket.id);
            if (t2) fastify.io.to(`ticket:${ticket.id}`).emit('ticket:updated', { ticket: t2 });
          }
        })
        .catch((err: unknown) => fastify.log.warn({ err }, '[internal] channel alerts failed'));
    }

    // Fresh channel row (settings + status) so preset/onboarding matches what admins just saved
    const channelForTalkback = await channelService.getById(channelId);
    if (channelForTalkback) {
      talkbackService
        .handleInbound({
          io: fastify.io,
          log: fastify.log,
          ticket,
          channel: channelForTalkback,
          contactPhone: contact.phone_number,
          inboundText: text,
        })
        .catch(err => {
          fastify.log.error({ err, ticketId: ticket.id }, '[internal] talkback failed');
        });
    } else {
      fastify.log.error({ channelId }, '[internal] channel row missing after persist; skip talkback');
    }

    return reply.send({ ok: true, ticketId: ticket.id, messageId: message.id });
  });

  // POST /internal/ws-event  — worker forwards WS events to connected browsers
  fastify.post('/ws-event', async (req, reply) => {
    const { event, payload } = req.body as { event: string; payload: unknown };
    fastify.io.emit(event, payload);
    return reply.send({ ok: true });
  });

  // POST /internal/channel-status  — worker reports channel connect/disconnect + saves session
  fastify.post('/channel-status', async (req, reply) => {
    const { channelId, status, sessionData, reason } = req.body as {
      channelId: string;
      status: 'connected' | 'disconnected' | null;
      sessionData?: unknown;
      reason?: string;
    };

    if (status) {
      await channelService.setStatus(channelId, status);
      if (status === 'connected') {
        fastify.io.emit('channel:connected', { channelId });
        await auditService.log(null, 'system', 'channel.connected', 'channel', channelId, {});
      } else {
        fastify.io.emit('channel:disconnected', { channelId, reason: reason ?? 'Unknown' });
        await auditService.log(null, 'system', 'channel.disconnected', 'channel', channelId, { reason });
      }
    }

    if (sessionData !== undefined) {
      await channelService.saveSession(channelId, sessionData);
    }

    return reply.send({ ok: true });
  });

  // POST /internal/worker-ready  — worker notifies API it has restarted; re-restore all sessions
  fastify.post('/worker-ready', async (_req, reply) => {
    fastify.log.info('[Internal] Worker signalled ready — restoring sessions');
    channelService.restoreAllSessions(fastify.io).catch(err => {
      fastify.log.warn({ err }, 'Session restore after worker-ready failed');
    });
    return reply.send({ ok: true });
  });
}
