import {
  Body,
  Controller,
  Get,
  Param,
  ParseUUIDPipe,
  Patch,
  Post,
  Query,
  UseGuards,
} from '@nestjs/common';
import { ChangeRequestStatus } from '../../entities/booking-change-request.entity';
import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
import { PermissionGuard } from '../../auth/guards/permission.guard';
import { Permissions } from '../../auth/decorators/permissions.decorator';
import { AccessLevel, ModuleName } from '../../entities/role-permission.entity';
import { successResponse, paginatedResponse } from '../../common/responses/api-response';
import { ChangeRequestService } from './change-request.service';
import { CreateChangeRequestDto } from './dto/create-change-request.dto';
import { DecideChangeRequestDto } from './dto/decide-change-request.dto';

/**
 * Two resource roots:
 *   POST   /bookings/:bookingId/change-requests       — sales submits
 *   GET    /bookings/:bookingId/change-requests       — per-booking log
 *   GET    /change-requests/pending                   — Approvals queue
 *   GET    /change-requests/:id                       — detail
 *   PATCH  /change-requests/:id/decide                — manager decides
 *
 * Submit requires the caller to have BOOKING edit. Decide requires
 * BOOKING_APPROVAL edit. Viewing the pending queue requires
 * BOOKING_APPROVAL view (so trip coordinators can observe).
 */
@Controller()
@UseGuards(JwtAuthGuard, PermissionGuard)
export class ChangeRequestController {
  constructor(private readonly service: ChangeRequestService) {}

  @Post('bookings/:bookingId/change-requests')
  @Permissions({ module: ModuleName.BOOKING, accessLevel: AccessLevel.EDIT })
  async create(
    @Param('bookingId', ParseUUIDPipe) bookingId: string,
    @Body() dto: CreateChangeRequestDto,
  ) {
    const result = await this.service.create(bookingId, dto);
    return successResponse(result, result.message);
  }

  @Get('bookings/:bookingId/change-requests')
  @Permissions({ module: ModuleName.BOOKING, accessLevel: AccessLevel.VIEW })
  async listByBooking(@Param('bookingId', ParseUUIDPipe) bookingId: string) {
    const data = await this.service.findByBooking(bookingId);
    return successResponse(data, 'Change requests fetched');
  }

  @Get('change-requests/pending')
  @Permissions({ module: ModuleName.BOOKING_APPROVAL, accessLevel: AccessLevel.VIEW })
  async listPending() {
    const data = await this.service.findPending();
    return successResponse(data, 'Pending change requests fetched');
  }

  /**
   * Full queue across all statuses, with optional ?status= filter.
   * Feeds the Approvals page's status-tab view so managers can see
   * decided requests too (Approved / Rejected / On Hold / Withdrawn).
   * Sort behaviour: oldest-first for ?status=pending so urgent items
   * surface; newest-first otherwise so the most recent decision is
   * up top.
   */
  @Get('change-requests')
  @Permissions({ module: ModuleName.BOOKING_APPROVAL, accessLevel: AccessLevel.VIEW })
  async listAll(
    @Query('status') status?: string,
    @Query('page') page?: string,
    @Query('limit') limit?: string,
  ) {
    const allowed = Object.values(ChangeRequestStatus) as string[];
    const filter = status && allowed.includes(status) ? (status as ChangeRequestStatus) : undefined;
    const result = await this.service.findAll(filter, {
      page: page ? Number(page) : undefined,
      limit: limit ? Number(limit) : undefined,
    });
    return paginatedResponse(result.items, result.meta, 'Change requests fetched');
  }

  @Get('change-requests/:id')
  @Permissions({ module: ModuleName.BOOKING_APPROVAL, accessLevel: AccessLevel.VIEW })
  async findOne(@Param('id', ParseUUIDPipe) id: string) {
    const data = await this.service.findById(id);
    return successResponse(data, 'Change request fetched');
  }

  @Patch('change-requests/:id/decide')
  @Permissions({ module: ModuleName.BOOKING_APPROVAL, accessLevel: AccessLevel.EDIT })
  async decide(@Param('id', ParseUUIDPipe) id: string, @Body() dto: DecideChangeRequestDto) {
    const result = await this.service.decide(id, dto);
    return successResponse(result, result.message);
  }

  /** Requester cancels their own pending request. Gated by booking:edit
   *  (same bar as creating) so the original requester — who obviously
   *  had that access — can always undo. Identity check is enforced in
   *  the service (requester_id must match CLS user_id). */
  @Patch('change-requests/:id/withdraw')
  @Permissions({ module: ModuleName.BOOKING, accessLevel: AccessLevel.EDIT })
  async withdraw(@Param('id', ParseUUIDPipe) id: string) {
    const result = await this.service.withdraw(id);
    return successResponse(result, result.message);
  }
}
