import { Router } from 'express';
import { z } from 'zod';
import { requireAdmin, requireAuth, type AuthenticatedRequest } from './auth';
import { pool } from './db';
import {
  createAdminObserverAdmission,
  endLiveKitRoom,
  liveKitReady,
} from './liveKitMedia';

export const adminCallsRouter = Router();

const meetingIdSchema = z.uuid();

const activeMeeting = async (meetingId: string) => {
  const result = await pool.query<{
    meetingId: string;
    journeyId: string;
    startsAt: Date;
    callEndedAt: Date | null;
  }>(
    `SELECT m.id AS "meetingId",m.journey_id AS "journeyId",m.starts_at AS "startsAt",
            m.call_ended_at AS "callEndedAt"
     FROM meeting_proposals m
     WHERE m.id=$1 AND m.status='confirmed'`,
    [meetingId],
  );
  return result.rows[0] ?? null;
};

adminCallsRouter.get('/calls', requireAuth, requireAdmin,
  async (_request: AuthenticatedRequest, response) => {
    const result = await pool.query(
      `SELECT m.id AS "meetingId",m.journey_id AS "journeyId",m.starts_at AS "startsAt",
              MIN(s.joined_at) AS "openedAt",
              COUNT(*) FILTER (
                WHERE s.left_at IS NULL AND s.last_seen_at >= NOW()-INTERVAL '30 seconds'
              )::int AS "participantCount",
              COALESCE(JSON_AGG(DISTINCT u.display_name) FILTER (
                WHERE s.left_at IS NULL AND s.last_seen_at >= NOW()-INTERVAL '30 seconds'
              ),'[]'::json) AS participants,
              c.solo_since AS "soloSince"
       FROM meeting_proposals m
       JOIN meeting_call_sessions s ON s.meeting_id=m.id
       JOIN users u ON u.id=s.user_id
       LEFT JOIN meeting_call_controls c ON c.meeting_id=m.id
       WHERE m.status='confirmed' AND m.call_ended_at IS NULL
       GROUP BY m.id,c.solo_since
       HAVING COUNT(*) FILTER (
         WHERE s.left_at IS NULL AND s.last_seen_at >= NOW()-INTERVAL '30 seconds'
       ) > 0
       ORDER BY MIN(s.joined_at) DESC`,
    );
    response.json({ calls: result.rows, mediaConfigured: liveKitReady() });
  });

adminCallsRouter.post('/calls/:meetingId/observe', requireAuth, requireAdmin,
  async (request: AuthenticatedRequest, response) => {
    const meetingId = meetingIdSchema.safeParse(request.params.meetingId);
    if (!meetingId.success) {
      response.status(400).json({ error: 'Invalid meeting ID' }); return;
    }
    if (!liveKitReady()) {
      response.status(503).json({ error: 'LiveKit Cloud is not configured' }); return;
    }
    const meeting = await activeMeeting(meetingId.data);
    if (!meeting || meeting.callEndedAt) {
      response.status(404).json({ error: 'Open call not found' }); return;
    }
    const active = await pool.query(
      `SELECT 1 FROM meeting_call_sessions
       WHERE meeting_id=$1 AND left_at IS NULL
         AND last_seen_at >= NOW()-INTERVAL '30 seconds' LIMIT 1`,
      [meetingId.data],
    );
    if (!active.rowCount) {
      response.status(409).json({ error: 'Call has no active participants' }); return;
    }
    const media = await createAdminObserverAdmission({
      meetingId: meetingId.data,
      adminUserId: request.authUser!.id,
      displayName: request.authUser!.displayName,
    });
    await pool.query(
      `INSERT INTO audit_events (user_id,event_type,details)
       VALUES ($1,'admin_call_observer_joined',$2::jsonb)`,
      [request.authUser!.id, JSON.stringify({
        meetingId: meetingId.data,
        journeyId: meeting.journeyId,
      })],
    );
    response.status(201).json({ media });
  });

adminCallsRouter.post('/calls/:meetingId/end', requireAuth, requireAdmin,
  async (request: AuthenticatedRequest, response) => {
    const meetingId = meetingIdSchema.safeParse(request.params.meetingId);
    if (!meetingId.success) {
      response.status(400).json({ error: 'Invalid meeting ID' }); return;
    }
    const meeting = await activeMeeting(meetingId.data);
    if (!meeting) {
      response.status(404).json({ error: 'Call not found' }); return;
    }
    if (!meeting.callEndedAt && liveKitReady()) {
      await endLiveKitRoom(meetingId.data);
    }
    const client = await pool.connect();
    try {
      await client.query('BEGIN');
      await client.query(
        `UPDATE meeting_proposals
         SET call_ended_at=COALESCE(call_ended_at,NOW()),call_ended_by=COALESCE(call_ended_by,$2)
         WHERE id=$1`,
        [meetingId.data, request.authUser!.id],
      );
      await client.query(
        `UPDATE meeting_call_sessions SET left_at=COALESCE(left_at,NOW()),forced_reason='admin_ended'
         WHERE meeting_id=$1`,
        [meetingId.data],
      );
      await client.query(
        `INSERT INTO audit_events (user_id,event_type,details)
         VALUES ($1,'admin_family_call_ended',$2::jsonb)`,
        [request.authUser!.id, JSON.stringify({
          meetingId: meetingId.data,
          journeyId: meeting.journeyId,
        })],
      );
      await client.query('COMMIT');
    } catch (error) {
      await client.query('ROLLBACK');
      throw error;
    } finally {
      client.release();
    }
    response.json({ ended: true });
  });
