import {
  Body,
  Controller,
  Delete,
  Get,
  HttpStatus,
  Ip,
  Param,
  Patch,
  Post,
  Query,
  Req,
  Res,
  UseGuards,
  UsePipes,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { getPermissionContext } from '../../common/access-control/permission-request.util';
import { CurrentUser } from '../../common/decorators/current-user.decorator';
import { ZodValidationPipe } from '../../common/pipes/zod-validation.pipe';
import { JwtAuthGuard } from '../owner/guards/jwt.guard';
import {
  AdminManualAttendanceDto,
  AttendanceFilterDto,
  AttendanceReportFilterDto,
  CreateQrPointDto,
  ScanQrAttendanceDto,
  TeacherCheckInDto,
  TeacherCheckOutDto,
  UpdateQrPointDto,
  adminManualAttendanceSchema,
  attendanceFilterSchema,
  attendanceReportFilterSchema,
  createQrPointSchema,
  scanQrAttendanceSchema,
  teacherCheckInSchema,
  teacherCheckOutSchema,
  updateQrPointSchema,
} from './attendance.dto';
import { AttendanceService } from './attendance.service';

function extractClientIp(req: Request, fallbackIp?: string): string {
  const forwarded = req.headers['x-forwarded-for'];
  if (typeof forwarded === 'string') {
    return forwarded.split(',')[0].trim();
  }
  if (Array.isArray(forwarded) && forwarded.length > 0) {
    return forwarded[0].trim();
  }
  return req.ip || req.socket.remoteAddress || fallbackIp || '127.0.0.1';
}

@Controller('attendance')
@UseGuards(JwtAuthGuard)
export class AttendanceController {
  constructor(private readonly service: AttendanceService) {}

  // =========================================================================
  // TEACHER QR SCAN & MANUAL ENDPOINTS
  // =========================================================================

  /**
   * Teacher scans static QR code to automatically check in or check out
   */
  @Post('scan-qr')
  @UsePipes(new ZodValidationPipe(scanQrAttendanceSchema))
  async scanQr(
    @Body() body: ScanQrAttendanceDto,
    @CurrentUser() owner_id: string,
    @Req() req: Request,
    @Ip() ip: string,
    @Res() res: Response,
  ) {
    const user: any = req.user;
    const memberId = user?.sub || user?.id;
    const clientIp = extractClientIp(req, ip);

    const result = await this.service.scanQr(
      memberId,
      owner_id,
      body,
      clientIp,
    );
    return res.status(result.status).json(result);
  }

  /**
   * Teacher Manual Check-In endpoint
   */
  @Post('check-in')
  @UsePipes(new ZodValidationPipe(teacherCheckInSchema))
  async checkIn(
    @Body() body: TeacherCheckInDto,
    @CurrentUser() owner_id: string,
    @Req() req: Request,
    @Ip() ip: string,
    @Res() res: Response,
  ) {
    const user: any = req.user;
    const memberId = user?.sub || user?.id;
    const clientIp = extractClientIp(req, ip);

    const result = await this.service.checkIn(
      memberId,
      owner_id,
      body,
      clientIp,
    );
    return res.status(result.status).json(result);
  }

  /**
   * Teacher Manual Check-Out endpoint
   */
  @Post('check-out')
  @UsePipes(new ZodValidationPipe(teacherCheckOutSchema))
  async checkOut(
    @Body() body: TeacherCheckOutDto,
    @CurrentUser() owner_id: string,
    @Req() req: Request,
    @Ip() ip: string,
    @Res() res: Response,
  ) {
    const user: any = req.user;
    const memberId = user?.sub || user?.id;
    const clientIp = extractClientIp(req, ip);

    const result = await this.service.checkOut(
      memberId,
      owner_id,
      body,
      clientIp,
    );
    return res.status(result.status).json(result);
  }

  // =========================================================================
  // ADMIN QR CODE MANAGEMENT
  // =========================================================================

  /**
   * Admin downloads/views the primary static QR code
   */
  @Get('qr/primary')
  async getPrimaryQr(
    @CurrentUser() owner_id: string,
    @Res() res: Response,
  ) {
    const result = await this.service.getPrimaryQrPoint(owner_id);
    return res.status(result.status).json(result);
  }

  /**
   * Admin lists all static QR points
   */
  @Get('qr/list')
  async listQrPoints(
    @CurrentUser() owner_id: string,
    @Res() res: Response,
  ) {
    const result = await this.service.listQrPoints(owner_id);
    return res.status(result.status).json(result);
  }

  /**
   * Admin creates a new QR point
   */
  @Post('qr')
  @UsePipes(new ZodValidationPipe(createQrPointSchema))
  async createQrPoint(
    @Body() body: CreateQrPointDto,
    @CurrentUser() owner_id: string,
    @Res() res: Response,
  ) {
    const result = await this.service.createQrPoint(owner_id, body);
    return res.status(result.status).json(result);
  }

  /**
   * Admin updates a QR point
   */
  @Patch('qr/:id')
  @UsePipes(new ZodValidationPipe(updateQrPointSchema))
  async updateQrPoint(
    @Param('id') id: string,
    @Body() body: UpdateQrPointDto,
    @CurrentUser() owner_id: string,
    @Res() res: Response,
  ) {
    const result = await this.service.updateQrPoint(owner_id, id, body);
    return res.status(result.status).json(result);
  }

  /**
   * Admin deletes a QR point
   */
  @Delete('qr/:id')
  async deleteQrPoint(
    @Param('id') id: string,
    @CurrentUser() owner_id: string,
    @Res() res: Response,
  ) {
    const result = await this.service.deleteQrPoint(owner_id, id);
    return res.status(result.status).json(result);
  }

  // =========================================================================
  // ADMIN MANUAL ATTENDANCE & QUERY ENDPOINTS
  // =========================================================================

  /**
   * Admin Manual Attendance (create/update)
   */
  @Post('create-or-update')
  @Post('admin/manual')
  @UsePipes(new ZodValidationPipe(adminManualAttendanceSchema))
  async createOrUpdate(
    @Body() data: AdminManualAttendanceDto,
    @CurrentUser() owner_id: string,
    @Req() req: Request,
    @Res() res: Response,
  ) {
    const result = await this.service.createOrUpdateAttendance(
      data,
      owner_id,
      getPermissionContext(req.user as any),
    );
    return res.status(HttpStatus.OK).json(result);
  }

  @Get('all')
  @UsePipes(new ZodValidationPipe(attendanceFilterSchema))
  async getAll(
    @Query() query: AttendanceFilterDto,
    @CurrentUser() owner_id: string,
    @Req() req: Request,
    @Res() res: Response,
  ) {
    const result = await this.service.getAttendances(
      query,
      owner_id,
      getPermissionContext(req.user as any),
    );
    return res.status(HttpStatus.OK).json(result);
  }

  @Get('today')
  @UsePipes(new ZodValidationPipe(attendanceFilterSchema))
  async getToday(
    @CurrentUser() owner_id: string,
    @Query() query: AttendanceFilterDto,
    @Req() req: Request,
    @Res() res: Response,
  ) {
    const result = await this.service.getTodayAttendances(
      owner_id,
      query,
      getPermissionContext(req.user as any),
    );
    return res.status(HttpStatus.OK).json(result);
  }

  @Get('find/:attendanceId')
  async find(
    @Param('attendanceId') attendanceId: string,
    @CurrentUser() owner_id: string,
    @Req() req: Request,
    @Res() res: Response,
  ) {
    const result = await this.service.getAttendanceById(
      attendanceId,
      owner_id,
      getPermissionContext(req.user as any),
    );
    return res.status(HttpStatus.OK).json(result);
  }

  @Get('member/:memberId')
  @UsePipes(new ZodValidationPipe(attendanceFilterSchema))
  async findByMember(
    @Param('memberId') memberId: string,
    @CurrentUser() owner_id: string,
    @Query() query: AttendanceFilterDto,
    @Req() req: Request,
    @Res() res: Response,
  ) {
    const result = await this.service.getAttendancesByMember(
      memberId,
      query,
      owner_id,
      getPermissionContext(req.user as any),
    );
    return res.status(HttpStatus.OK).json(result);
  }

  @Get('today/:memberId')
  @UsePipes(new ZodValidationPipe(attendanceFilterSchema))
  async findByTodayMember(
    @Param('memberId') memberId: string,
    @Query('date') date: string,
    @CurrentUser() owner_id: string,
    @Req() req: Request,
    @Res() res: Response,
  ) {
    const result = await this.service.getTodayAttendancesByMember(
      memberId,
      owner_id,
      date,
      getPermissionContext(req.user as any),
    );
    return res.status(HttpStatus.OK).json(result);
  }

  @Get('report')
  @UsePipes(new ZodValidationPipe(attendanceReportFilterSchema))
  async getReport(
    @Query() query: AttendanceReportFilterDto,
    @CurrentUser() owner_id: string,
    @Req() req: Request,
    @Res() res: Response,
  ) {
    const result = await this.service.getAttendanceReport(
      query,
      owner_id,
      getPermissionContext(req.user as any),
    );
    return res.status(HttpStatus.OK).json(result);
  }
}
