import express, { Request, Response } from 'express';
import { Op } from 'sequelize';
import ExamMeeting from '../models/ExamMeeting';
import { isLiveKitConfigured, resolveLiveKitPublicUrl } from '../config/livekit';
import {
  buildExamRoomName,
  buildParticipantIdentity,
  createLiveKitAccessToken,
  ensureLiveKitRoom,
  invalidateLiveKitRoomCache,
  muteAllStudentMicrophones,
  muteStudentMicrophone,
} from '../services/livekit.service';
import {
  canJoinAsProctor,
  requirePhpAuth,
  resolveParticipantRole,
} from '../middleware/phpJwtAuth';
import { checkLiveKitHealth } from '../services/livekit-health.service';

const router = express.Router();

/** GET /api/livekit/status — ตรวจ config + การเชื่อมต่อ server (ไม่ต้อง auth) */
router.get('/status', async (_req, res) => {
  try {
    const health = await checkLiveKitHealth();
    const statusCode = health.configured && health.serverReachable ? 200 : 503;
    return res.status(statusCode).json({ success: health.serverReachable, data: health });
  } catch (error: unknown) {
    const message = error instanceof Error ? error.message : 'Unknown error';
    return res.status(500).json({ success: false, message });
  }
});

router.use((_req, res, next) => {
  if (!isLiveKitConfigured()) {
    res.status(503).json({
      success: false,
      message: 'LiveKit ยังไม่ได้ตั้งค่า (LIVEKIT_API_KEY / LIVEKIT_API_SECRET)',
    });
    return;
  }
  next();
});

async function issueToken(
  req: Request,
  res: Response,
  courseId: string,
  recid: string,
  itemid: string,
  roleQuery?: string,
): Promise<Response> {
  const authUser = req.authUser!;

  let participantRole: 'student' | 'proctor';
  try {
    participantRole = resolveParticipantRole(roleQuery, authUser.typeuser);
  } catch (e: unknown) {
    const message = e instanceof Error ? e.message : 'Forbidden';
    return res.status(403).json({ success: false, message });
  }

  const examMeeting = await ExamMeeting.findOne({
    where: {
      course_id: courseId,
      recid,
      itemid,
      is_active: true,
    },
    order: [['createdAt', 'DESC']],
  });

  if (!examMeeting?.meeting_id) {
    return res.status(404).json({
      success: false,
      message: 'ยังไม่มีห้องสอบสำหรับรายการนี้ — ให้ผู้ดูแลสร้างห้องก่อน',
    });
  }

  const roomName = examMeeting.meeting_id;
  await ensureLiveKitRoom(roomName);
  const identity = buildParticipantIdentity(participantRole, authUser.user_id);
  const token = await createLiveKitAccessToken({
    roomName,
    identity,
    role: participantRole,
    name: authUser.userid,
  });

  const livekit_url = resolveLiveKitPublicUrl(req);

  return res.json({
    success: true,
    data: {
      token,
      livekit_url,
      room_name: roomName,
      identity,
      role: participantRole,
      provider: 'livekit' as const,
      course_id: courseId,
      recid,
      itemid,
    },
  });
}

/** POST /api/livekit/room — สร้างห้อง LiveKit + บันทึก meeting_id */
router.post('/room', requirePhpAuth, async (req, res) => {
  try {
    const { course_id, recid, itemid } = req.body;
    const created_by = req.authUser!.user_id;

    if (!course_id || !recid || !itemid) {
      return res.status(400).json({
        success: false,
        message: 'course_id, recid, itemid are required',
      });
    }

    if (!canJoinAsProctor(req.authUser!.typeuser)) {
      return res.status(403).json({
        success: false,
        message: 'เฉพาะผู้ดูแล/กรรมการเท่านั้นที่สร้างห้องได้',
      });
    }

    const meeting_id = buildExamRoomName(course_id, recid, itemid);
    invalidateLiveKitRoomCache(meeting_id);
    await ensureLiveKitRoom(meeting_id);

    const existing = await ExamMeeting.findOne({
      where: { course_id, recid, itemid },
    });

    let examMeeting;
    if (existing) {
      await ExamMeeting.update(
        { is_active: false },
        {
          where: {
            course_id,
            recid,
            itemid,
            is_active: true,
            id: { [Op.ne]: existing.id },
          },
        },
      );
      await existing.update({
        meeting_id,
        created_by,
        is_active: true,
        provider: 'livekit',
      });
      examMeeting = existing;
    } else {
      examMeeting = await ExamMeeting.create({
        course_id,
        recid,
        itemid,
        meeting_id,
        provider: 'livekit',
        created_by,
        is_active: true,
      });
    }

    const livekit_url = resolveLiveKitPublicUrl(req);

    return res.json({
      success: true,
      data: {
        id: examMeeting.id,
        course_id: examMeeting.course_id,
        recid: examMeeting.recid,
        itemid: examMeeting.itemid,
        meeting_id: examMeeting.meeting_id,
        provider: examMeeting.provider,
        livekit_url,
      },
    });
  } catch (error: unknown) {
    const message = error instanceof Error ? error.message : 'Unknown error';
    console.error('POST /api/livekit/room', error);
    return res.status(500).json({ success: false, message });
  }
});

/** GET /api/livekit/token/:courseId/:recid/:itemid?role=student|proctor */
router.get('/token/:courseId/:recid/:itemid', requirePhpAuth, async (req, res) => {
  try {
    return await issueToken(
      req,
      res,
      req.params.courseId,
      req.params.recid,
      req.params.itemid,
      req.query.role as string | undefined,
    );
  } catch (error: unknown) {
    const message = error instanceof Error ? error.message : 'Unknown error';
    console.error('GET /api/livekit/token', error);
    return res.status(500).json({ success: false, message });
  }
});

/** POST /api/livekit/token */
router.post('/token', requirePhpAuth, async (req, res) => {
  try {
    const { course_id, recid, itemid, role } = req.body;
    if (!course_id || !recid || !itemid) {
      return res.status(400).json({
        success: false,
        message: 'course_id, recid, itemid are required',
      });
    }
    return await issueToken(
      req,
      res,
      String(course_id),
      String(recid),
      String(itemid),
      role ? String(role) : undefined,
    );
  } catch (error: unknown) {
    const message = error instanceof Error ? error.message : 'Unknown error';
    console.error('POST /api/livekit/token', error);
    return res.status(500).json({ success: false, message });
  }
});

/** POST /api/livekit/mute-participant — ผู้คุมสอบปิดไมค์ผู้สอบ (LiveKit server-side) */
router.post('/mute-participant', requirePhpAuth, async (req, res) => {
  try {
    if (!canJoinAsProctor(req.authUser!.typeuser)) {
      return res.status(403).json({
        success: false,
        message: 'เฉพาะผู้ดูแล/กรรมการเท่านั้นที่สั่งปิดไมค์ได้',
      });
    }

    const { room_name, user_id, mute_all } = req.body as {
      room_name?: string;
      user_id?: string;
      mute_all?: boolean;
    };

    if (!room_name?.trim()) {
      return res.status(400).json({
        success: false,
        message: 'room_name is required',
      });
    }

    const roomName = String(room_name).trim();

    if (mute_all) {
      const count = await muteAllStudentMicrophones(roomName);
      return res.json({
        success: true,
        data: { muted: count, room_name: roomName, mute_all: true },
      });
    }

    if (!user_id?.trim()) {
      return res.status(400).json({
        success: false,
        message: 'user_id is required when mute_all is false',
      });
    }

    const muted = await muteStudentMicrophone(roomName, String(user_id).trim());
    return res.json({
      success: true,
      data: {
        muted,
        room_name: roomName,
        user_id: String(user_id).trim(),
        identity: buildParticipantIdentity('student', String(user_id).trim()),
      },
    });
  } catch (error: unknown) {
    const message = error instanceof Error ? error.message : 'Unknown error';
    console.error('POST /api/livekit/mute-participant', error);
    return res.status(500).json({ success: false, message });
  }
});

export default router;
