import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  ParseIntPipe,
  Patch,
  Post,
  Query,
  Req,
  UseGuards,
} from '@nestjs/common';
import { MeetingService } from './services/meeting.service';
import { MeetingInviteService } from './services/meeting-invite.service';
import { CreateMeetingDto } from './dto/create-meeting.dto';
import { UpdateMeetingDto } from './dto/update-meeting.dto';
import { ListMeetingsQueryDto } from './dto/list-meetings.dto';
import { CreateInviteDto } from './dto/create-invite.dto';
import { SendInviteDto } from './dto/send-invite.dto';
import { EndMeetingDto } from './dto/end-meeting.dto';
import * as crypto from 'crypto';
import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
/** this app's JWT puts the id in `sub` in some places and `id` in others */
const uid = (req: any): number =>
  Number(req.user?.id ?? req.user?.sub ?? req.user?.userId);

@Controller('meetings')
export class MeetingController {
  constructor(
    private readonly meetings: MeetingService,
    private readonly invites: MeetingInviteService,
  ) {}

  // ── meetings ───────────────────────────────────────────────────────────

  @Post()
  create(@Body() dto: CreateMeetingDto, @Req() req: any) {
    return this.meetings.create(dto, uid(req));
  }

  /** One tap from the audit page — reuses an open meeting if there is one */
  @Post('for-audit/:auditRowId')
  createForAudit(
    @Param('auditRowId', ParseIntPipe) auditRowId: number,
    @Req() req: any,
  ) {
    return this.meetings.createForAudit(auditRowId, uid(req));
  }

  @Get()
  findAll(@Query() q: ListMeetingsQueryDto, @Req() req: any) {
    return this.meetings.findAll(q, uid(req));
  }

  @Get('by-audit/:auditRowId')
  findByAudit(@Param('auditRowId', ParseIntPipe) auditRowId: number) {
    return this.meetings.findByAudit(auditRowId);
  }

  @Get('by-code/:roomCode')
  findByRoomCode(@Param('roomCode') roomCode: string) {
    return this.meetings.findByRoomCode(roomCode);
  }
  @Get('turn-credentials')
  getTurnCredentials() {
    const secret = process.env.TURN_SECRET;
    const host = process.env.TURN_HOST;
    if (!secret || !host) {
      return { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] };
    }
    const ttl = 3600;
    const username = `${Math.floor(Date.now() / 1000) + ttl}`;
    const credential = crypto.createHmac('sha1', secret).update(username).digest('base64');
    return {
      iceServers: [
        { urls: `stun:${host}:3478` },
        { urls: [`turn:${host}:3478?transport=udp`, `turn:${host}:3478?transport=tcp`], username, credential },
      ],
    };
  }

  @Get(':id')
  findOne(@Param('id', ParseIntPipe) id: number) {
    return this.meetings.findOne(id);
  }

  @Patch(':id')
  update(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: UpdateMeetingDto,
    @Req() req: any,
  ) {
    return this.meetings.update(id, dto, uid(req));
  }

  @Post(':id/end')
  end(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: EndMeetingDto,
    @Req() req: any,
  ) {
    return this.meetings.end(id, dto, uid(req));
  }

  @Post(':id/cancel')
  cancel(@Param('id', ParseIntPipe) id: number, @Req() req: any) {
    return this.meetings.cancel(id, uid(req));
  }

  // ── invites ────────────────────────────────────────────────────────────

  /**
   * Create an invite. Always returns a whatsapp_url the caller can open —
   * that is the WhatsApp path, no Business API involved. Pass
   * send_now + channel EMAIL to have the server email it immediately.
   */
  @Post(':id/invites')
  async createInvite(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: CreateInviteDto,
    @Req() req: any,
  ) {
    const r = await this.invites.create(id, dto, uid(req));
    return {
      id: r.invite.id,
      meeting_id: r.invite.meeting_id,
      url: r.url,
      token: r.invite.token,
      scope: r.invite.scope,
      grant_role: r.invite.grant_role,
      expires_at: r.invite.expires_at,
      // present only for VERIFIED — returned once, stored hashed
      passcode: r.otp,
      whatsapp_url: r.whatsapp_url,
      sms_url: r.sms_url,
      message_text: r.message_text,
      email_sent: r.email_sent,
    };
  }

  /** Send (or re-send) an invite over a chosen channel */
  @Post('invites/:inviteId/send')
  async sendInvite(
    @Param('inviteId', ParseIntPipe) inviteId: number,
    @Body() dto: SendInviteDto,
    @Req() req: any,
  ) {
    const r = await this.invites.send(inviteId, dto, uid(req));
    return {
      id: r.invite.id,
      url: r.url,
      passcode: r.otp,
      whatsapp_url: r.whatsapp_url,
      sms_url: r.sms_url,
      message_text: r.message_text,
      email_sent: r.email_sent,
      status: r.invite.status,
      sent_at: r.invite.sent_at,
    };
  }

  @Get(':id/invites')
  listInvites(@Param('id', ParseIntPipe) id: number) {
    return this.invites.listForMeeting(id);
  }

  @Delete('invites/:inviteId')
  revoke(@Param('inviteId', ParseIntPipe) inviteId: number, @Req() req: any) {
    return this.invites.revoke(inviteId, uid(req));
  }
}
