import type { FastifyInstance } from 'fastify';
import { mergeChannelSettings, type ChannelSettings, isWebChatSystemChannel } from '@wa-ticketing/shared';
import { authenticate, requireRole } from '../middleware/auth.js';
import { channelService } from '../services/channelService.js';
import { auditService } from '../services/auditService.js';
import { assertCapability, redactInboundToken, resolveCreateProvider } from '../services/channelProviderRules.js';

const staff = [authenticate, requireRole('admin', 'user')];

export async function channelRoutes(fastify: FastifyInstance) {
  fastify.get('/', { preHandler: staff }, async (_req, reply) => {
    return reply.send(await channelService.list());
  });

  // POST /api/v1/channels/init  (Admin only — must register before /:id)
  fastify.post('/init', { preHandler: [authenticate, requireRole('admin')] }, async (req, reply) => {
    const { name, phone_number, provider: providerIn } = req.body as {
      name: string; phone_number: string; provider?: unknown;
    };
    if (!name || !phone_number) {
      return reply.status(400).send({ error: 'name and phone_number are required' });
    }

    let channel;
    try {
      channel = await channelService.create(name, phone_number, resolveCreateProvider(providerIn));
    } catch (e: unknown) {
      const err = e as { statusCode?: number; message?: string };
      if (!err.statusCode) throw e;
      return reply.status(err.statusCode).send({ error: err.message });
    }
    await auditService.log(req.user!.sub, 'user', 'channel.init_started', 'channel', channel.id, {
      name, provider: channel.provider,
    });

    // Only QR-paired providers (Baileys) have a worker session to start
    if (channel.capabilities?.qr_pairing) {
      channelService.startSession(channel.id, fastify.io).catch(err => {
        fastify.log.error({ err }, `Failed to start session for channel ${channel.id}`);
      });
    }

    return reply.status(201).send(channel);
  });

  // POST /api/v1/channels/email — inbound-via-webhook email channel (SMTP replies; no POP/IMAP worker)
  fastify.post('/email', { preHandler: [authenticate, requireRole('admin')] }, async (req, reply) => {
    const { name } = req.body as { name?: string };
    if (!name?.trim()) return reply.status(400).send({ error: 'name is required' });
    const channel = await channelService.createEmailChannel(name.trim());
    await auditService.log(req.user!.sub, 'user', 'channel.email_created', 'channel', channel.id, { name: name.trim() });
    return reply.status(201).send(channel);
  });

  // POST /api/v1/channels/:id/reconnect  (Admin — relink disconnected channel; starts worker session + QR via Socket.IO)
  fastify.post('/:id/reconnect', { preHandler: [authenticate, requireRole('admin')] }, async (req, reply) => {
    const { id } = req.params as { id: string };
    const ch = await channelService.getById(id);
    if (!ch) return reply.status(404).send({ error: 'Channel not found' });
    if (isWebChatSystemChannel(ch.phone_number) || ch.kind === 'email') {
      return reply.status(400).send({ error: 'This channel does not use WhatsApp — no reconnect needed.' });
    }

    // Capability check first: providers without sessions must not lose state or change status
    try {
      assertCapability(ch, 'session_reconnect');
    } catch (e: unknown) {
      const err = e as { message: string; code?: string };
      return reply.status(400).send({ error: err.message, code: err.code });
    }

    await channelService.clearWorkerSession(id);
    await channelService.setStatus(id, 'connecting');
    await auditService.log(req.user!.sub, 'user', 'channel.reconnect_started', 'channel', id, {});

    try {
      // clearAuth: wipe stale Baileys creds so the worker emits a new QR (not silent re-login)
      await channelService.startSession(id, fastify.io, { clearAuth: true });
    } catch (err) {
      fastify.log.error({ err }, `Reconnect: worker failed for channel ${id}`);
      await channelService.setStatus(id, 'disconnected');
      return reply.status(503).send({
        error: 'WhatsApp worker unavailable. Ensure the worker service is running (e.g. docker compose up whatsapp).',
      });
    }

    const updated = await channelService.getById(id);
    return reply.send(updated ?? ch);
  });

  // GET /api/v1/channels/:id
  fastify.get('/:id', { preHandler: staff }, async (req, reply) => {
    const { id } = req.params as { id: string };
    const ch = await channelService.getById(id);
    if (!ch) return reply.status(404).send({ error: 'Channel not found' });
    return reply.send(redactInboundToken(ch, req.user!.role));
  });

  // PATCH /api/v1/channels/:id  (Admin — name + settings)
  fastify.patch('/:id', { preHandler: [authenticate, requireRole('admin')] }, async (req, reply) => {
    const { id } = req.params as { id: string };
    const body = req.body as { name?: string; settings?: ChannelSettings };
    try {
      const updated = await channelService.update(
        id,
        {
          name: body.name,
          settings: body.settings !== undefined ? mergeChannelSettings(body.settings) : undefined,
        },
        req.user!.sub,
      );
      if (!updated) return reply.status(404).send({ error: 'Channel not found' });
      return reply.send(updated);
    } catch (e: unknown) {
      const err = e as { statusCode?: number; message?: string };
      return reply.status(err.statusCode ?? 500).send({ error: err.message ?? 'Failed to update channel' });
    }
  });

  // DELETE /api/v1/channels/:id  (Admin only)
  fastify.delete('/:id', { preHandler: [authenticate, requireRole('admin')] }, async (req, reply) => {
    const { id } = req.params as { id: string };
    await channelService.remove(id, req.user!.sub);
    fastify.io.emit('channel:disconnected', { channelId: id, reason: 'Channel removed' });
    return reply.status(204).send();
  });
}
