import express, { Router, type Request } from 'express';
import { WebhookReceiver } from 'livekit-server-sdk';
import { config } from './config';
import { pool } from './db';
import { recordJourneyEvent } from './relationshipEvents';

export const liveKitWebhookRouter = Router();

interface ParticipantMetadata {
  sessionId?: string;
  userId?: string;
  observer?: boolean;
  role?: 'groom' | 'groomGuardian' | 'bride' | 'brideGuardian';
}

const metadataFrom = (value: string): ParticipantMetadata => {
  try {
    return JSON.parse(value || '{}') as ParticipantMetadata;
  } catch {
    return {};
  }
};

const meetingIdFrom = (roomName?: string): string | null => {
  if (!roomName?.startsWith('family-')) return null;
  return roomName.slice('family-'.length);
};

const receiver = () => {
  const { apiKey, apiSecret } = config.liveKit;
  if (!apiKey || !apiSecret) throw new Error('LiveKit Cloud is not configured');
  return new WebhookReceiver(apiKey, apiSecret);
};

liveKitWebhookRouter.post(
  '/livekit/webhook',
  express.raw({ type: 'application/webhook+json', limit: '256kb' }),
  async (request: Request, response) => {
    if (!config.liveKit.configured) {
      response.status(503).json({ error: 'LiveKit Cloud is not configured' }); return;
    }
    if (!Buffer.isBuffer(request.body)) {
      response.status(415).json({ error: 'Raw webhook body required' }); return;
    }
    const event = await receiver().receive(
      request.body.toString('utf8'),
      request.header('authorization'),
    );
    const meetingId = meetingIdFrom(event.room?.name);
    if (!meetingId) {
      response.status(202).json({ accepted: true }); return;
    }

    const client = await pool.connect();
    let disconnectedUserId: string | null = null;
    let disconnectedRole: ParticipantMetadata['role'];
    try {
      await client.query('BEGIN');
      const inserted = await client.query(
        `INSERT INTO livekit_webhook_events (id,event_type) VALUES ($1,$2)
         ON CONFLICT (id) DO NOTHING RETURNING id`,
        [event.id, event.event],
      );
      if (!inserted.rowCount) {
        await client.query('ROLLBACK');
        response.json({ accepted: true, duplicate: true }); return;
      }

      const metadata = metadataFrom(event.participant?.metadata ?? '');
      if (!metadata.observer && metadata.sessionId) {
        if (event.event === 'participant_joined') {
          await client.query(
            `UPDATE meeting_call_sessions
             SET provider_joined_at=COALESCE(provider_joined_at,NOW()),last_seen_at=NOW()
             WHERE meeting_id=$1 AND session_id=$2 AND user_id=$3 AND left_at IS NULL`,
            [meetingId, metadata.sessionId, metadata.userId],
          );
        }
        if (event.event === 'participant_left'
          || event.event === 'participant_connection_aborted') {
          const disconnected = await client.query<{ user_id: string }>(
            `UPDATE meeting_call_sessions SET left_at=COALESCE(left_at,NOW())
             WHERE meeting_id=$1 AND session_id=$2 AND left_at IS NULL RETURNING user_id`,
            [meetingId, metadata.sessionId],
          );
          disconnectedUserId = disconnected.rows[0]?.user_id ?? null;
          disconnectedRole = metadata.role;
        }
      }
      if (event.event === 'room_finished') {
        await client.query(
          `UPDATE meeting_call_sessions SET left_at=COALESCE(left_at,NOW())
           WHERE meeting_id=$1`,
          [meetingId],
        );
        await client.query(
          `UPDATE meeting_call_controls
           SET solo_identity=NULL,solo_since=NULL,updated_at=NOW() WHERE meeting_id=$1`,
          [meetingId],
        );
      }
      await client.query('COMMIT');
      if (disconnectedUserId) {
        const journey = await pool.query<{ journey_id: string }>(
          'SELECT journey_id FROM meeting_proposals WHERE id=$1', [meetingId],
        );
        const roleLabels = {
          groom: 'العريس', groomGuardian: 'ولي أمر العريس',
          bride: 'العروس', brideGuardian: 'ولي أمر العروس',
        } as const;
        if (journey.rows[0]) await recordJourneyEvent(
          journey.rows[0].journey_id, disconnectedUserId, 'call_left', {
            meetingId, role: disconnectedRole,
            roleLabel: disconnectedRole ? roleLabels[disconnectedRole] : undefined,
          },
        );
      }
      response.json({ accepted: true });
    } catch (error) {
      await client.query('ROLLBACK');
      throw error;
    } finally {
      client.release();
    }
  },
);
