import type { FastifyBaseLogger } from 'fastify';
import type { Server as SocketServer } from 'socket.io';
import { defaultTicketIntake } from '@wa-ticketing/shared';
import { contactService } from './contactService.js';
import { companyService } from './companyService.js';
import { ticketService } from './ticketService.js';
import { messagingService } from './messagingService.js';
import { auditService } from './auditService.js';
import { channelService } from './channelService.js';
import { talkbackService } from './talkbackService.js';
import { notifyInboundAlerts } from './channelAlertService.js';

export const portalService = {
  /**
   * Company user sends a message into support: append inbound (portal) on open ticket or create one.
   * Runs the same onboarding / AI talkback as WhatsApp (settings inherited from primary WA channel on web chat).
   */
  async postInboundFromCompanyUser(opts: {
    io: SocketServer;
    log: FastifyBaseLogger;
    userId: string;
    companyId: string;
    content: string;
    ticketId?: string;
  }) {
    const { io, log, userId, companyId, content, ticketId } = opts;
    const text = content.trim();
    if (!text) throw Object.assign(new Error('content is required'), { statusCode: 400 });

    const portalChannelId = await companyService.getPortalChannelId();
    const portalChannel = await channelService.getById(portalChannelId);
    if (!portalChannel?.settings) {
      throw Object.assign(new Error('Web chat channel not configured'), { statusCode: 500 });
    }

    const contact = await contactService.ensurePortalUser(userId, companyId);

    let ticket = ticketId
      ? await ticketService.getById(ticketId)
      : await ticketService.findOpenTicket(contact.id, portalChannelId);

    if (ticketId) {
      if (!ticket) throw Object.assign(new Error('Ticket not found'), { statusCode: 404 });
      if (ticket.contact_id !== contact.id || ticket.channel_id !== portalChannelId) {
        throw Object.assign(new Error('Forbidden'), { statusCode: 403 });
      }
      if (ticket.status !== 'open') {
        throw Object.assign(new Error('Ticket is closed'), { statusCode: 400 });
      }
    } else if (!ticket) {
      const priorCount = await ticketService.countForContactChannel(contact.id, portalChannelId);
      const intake =
        priorCount === 0
          ? ticketService.buildInitialIntake(portalChannel.settings, true)
          : defaultTicketIntake;
      ticket = await ticketService.createTicket(contact.id, portalChannelId, intake);
      const hydrated = await ticketService.getById(ticket.id);
      io.emit('ticket:created', { ticket: hydrated ?? ticket });
    }

    const message = await messagingService.appendMessage(
      ticket!.id,
      'inbound',
      text,
      null,
      undefined,
      'portal',
    );

    await auditService.log(userId, 'user', 'portal.message', 'message', message.id, {
      ticketId: ticket!.id,
    });

    io.to(`ticket:${ticket!.id}`).emit('message:new', { ticketId: ticket!.id, message });

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

    const ticketFresh = await ticketService.getById(ticket!.id);
    if (ticketFresh) {
      talkbackService
        .handleInbound({
          io,
          log,
          ticket: ticketFresh,
          channel: portalChannel,
          contactPhone: contact.phone_number,
          inboundText: text,
        })
        .catch(err => {
          log.error({ err, ticketId: ticket!.id }, '[portal] talkback failed');
        });
    }

    return { ticket: ticket!, message };
  },
};
