import {
  Controller,
  Get,
  Post,
  Patch,
  Delete,
  Body,
  Param,
  Query,
  ParseIntPipe,
  Req,
  Logger,
} from '@nestjs/common';
import type { Request } from 'express';

import { AuditSchedulesService } from './services/audit-schedules.service';
import {
  CreateAuditScheduleDto,
  CreateAuditRowDto,
} from './dto/create-audit-schedule.dto';
import { UpdateAuditScheduleDto } from './dto/update-audit-schedule.dto';
import {
  UpdateAuditRowDto,
  CancelAuditRowDto,
  RescheduleAuditRowDto,
  BulkCancelDto,
} from './dto/audit-row.dto';
import { ListSchedulesQueryDto } from './dto/list-query.dto';

@Controller('audit-schedules')
export class AuditSchedulesController {
  // NestJS logger — appears in pm2 logs with module name prefix
  private readonly logger = new Logger('AuditSchedulesController');

  constructor(private readonly service: AuditSchedulesService) {}
  //   - req.user.sub       (JWT standard claim)
  private getCurrentUserId(req: Request): number {
    const user = (req as any).user;
    const id = user?.id ?? user?.userId ?? user?.sub;
    if (id === undefined || id === null) {
      throw new Error(
        'User id not found on request.user (JwtAuthGuard / strategy issue?)',
      );
    }
    return Number(id);
  }

  // schedule only their own rows are returned.
  @Get()
  list(@Query() q: ListSchedulesQueryDto, @Req() req: Request) {
    const userId = this.getCurrentUserId(req);
    return this.service.findAll(q, userId);
  }

  // GET /api/audit-schedules/analytics
  @Get('analytics')
  analytics() {
    return this.service.getAnalytics();
  }

  // GET /api/audit-schedules/by-date/:date
  @Get('by-date/:date')
  byDate(@Param('date') date: string) {
    return this.service.findByDate(date);
  }

  // GET /api/audit-schedules/company/:companyId/history
  @Get('company/:companyId/history')
  companyHistory(
    @Param('companyId', ParseIntPipe) companyId: number,
    @Query('limit') limit?: string,
  ) {
    const n = limit ? parseInt(limit, 10) : 5;
    return this.service.getCompanyAuditHistory(companyId, n);
  }

  // GET /api/audit-schedules/:id
  // ✅ CHANGED: passes the current user id so the service can apply the
  // same auditor row-level filtering on the single-schedule detail view.
  @Get(':id')
  findOne(@Param('id', ParseIntPipe) id: number, @Req() req: Request) {
    const userId = this.getCurrentUserId(req);
    return this.service.findOne(id, undefined, userId);
  }

  // POST /api/audit-schedules
  @Post()
  async create(@Body() dto: CreateAuditScheduleDto, @Req() req: Request) {
    const userId = this.getCurrentUserId(req);
    this.logger.log(
      `POST /audit-schedules — user=${userId}, rows=${dto.rows?.length ?? 0}, date=${dto.schedule_date}`,
    );
    try {
      return await this.service.create(dto, userId);
    } catch (err: any) {
      this.logger.error(`POST /audit-schedules failed: ${err.message}`);
      throw err;
    }
  }

  // PATCH /api/audit-schedules/:id
  // ✅ CHANGED: passes the current user id so the service can enforce the
  // 'edit' (or 'publish') permission before updating.
  @Patch(':id')
  update(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: UpdateAuditScheduleDto,
    @Req() req: Request,
  ) {
    const userId = this.getCurrentUserId(req);
    this.logger.log(`PATCH /audit-schedules/${id} — user=${userId}`);
    return this.service.update(id, dto, userId);
  }

  // DELETE /api/audit-schedules/:id
  // ✅ CHANGED: passes the current user id so the service can enforce the
  // 'delete' permission before deleting.
  @Delete(':id')
  remove(@Param('id', ParseIntPipe) id: number, @Req() req: Request) {
    const userId = this.getCurrentUserId(req);
    this.logger.log(`DELETE /audit-schedules/${id} — user=${userId}`);
    return this.service.remove(id, userId);
  }

  // POST /api/audit-schedules/:id/bulk-cancel
  @Post(':id/bulk-cancel')
  bulkCancel(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: BulkCancelDto,
    @Req() req: Request,
  ) {
    const userId = this.getCurrentUserId(req);
    this.logger.log(
      `POST /audit-schedules/${id}/bulk-cancel — user=${userId}, reason=${dto.cancellation_reason}`,
    );
    return this.service.bulkCancel(id, dto, userId);
  }

  // ═══════════════════ ROWS (CHILDREN) ═══════════════════

  // POST /api/audit-schedules/:id/rows
  @Post(':id/rows')
  addRow(
    @Param('id', ParseIntPipe) scheduleId: number,
    @Body() dto: CreateAuditRowDto,
    @Req() req: Request,
  ) {
    const userId = this.getCurrentUserId(req);
    this.logger.log(
      `POST /audit-schedules/${scheduleId}/rows — user=${userId}`,
    );
    return this.service.addRow(scheduleId, dto, userId);
  }

  // PATCH /api/audit-schedules/:id/rows/:rowId
  @Patch(':id/rows/:rowId')
  updateRow(
    @Param('id', ParseIntPipe) scheduleId: number,
    @Param('rowId', ParseIntPipe) rowId: number,
    @Body() dto: UpdateAuditRowDto,
    @Req() req: Request,
  ) {
    const userId = this.getCurrentUserId(req);
    this.logger.log(
      `PATCH /audit-schedules/${scheduleId}/rows/${rowId} — user=${userId}`,
    );
    return this.service.updateRow(scheduleId, rowId, dto, userId);
  }

  // DELETE /api/audit-schedules/:id/rows/:rowId
  // ✅ CHANGED: passes the current user id so the service can enforce the
  // 'delete' permission before deleting the row.
  @Delete(':id/rows/:rowId')
  removeRow(
    @Param('id', ParseIntPipe) scheduleId: number,
    @Param('rowId', ParseIntPipe) rowId: number,
    @Req() req: Request,
  ) {
    const userId = this.getCurrentUserId(req);
    this.logger.log(
      `DELETE /audit-schedules/${scheduleId}/rows/${rowId} — user=${userId}`,
    );
    return this.service.removeRow(scheduleId, rowId, userId);
  }

  // POST /api/audit-schedules/rows/:rowId/cancel
  @Post('rows/:rowId/cancel')
  cancelRow(
    @Param('rowId', ParseIntPipe) rowId: number,
    @Body() dto: CancelAuditRowDto,
    @Req() req: Request,
  ) {
    const userId = this.getCurrentUserId(req);
    this.logger.log(
      `POST /audit-schedules/rows/${rowId}/cancel — user=${userId}, reason=${dto.cancellation_reason}`,
    );
    return this.service.cancelRow(rowId, dto, userId);
  }

  // POST /api/audit-schedules/rows/:rowId/reschedule
  @Post('rows/:rowId/reschedule')
  rescheduleRow(
    @Param('rowId', ParseIntPipe) rowId: number,
    @Body() dto: RescheduleAuditRowDto,
    @Req() req: Request,
  ) {
    const userId = this.getCurrentUserId(req);
    this.logger.log(
      `POST /audit-schedules/rows/${rowId}/reschedule — user=${userId}, newDate=${dto.new_audit_date}`,
    );
    return this.service.rescheduleRow(rowId, dto, userId);
  }

  // GET /api/audit-schedules/rows/:rowId/audit-trail
  @Get('rows/:rowId/audit-trail')
  rowAuditTrail(@Param('rowId', ParseIntPipe) rowId: number) {
    return this.service.getRowAuditTrail(rowId);
  }
}
