import {
  Controller,
  Get,
  Post,
  Patch,
  Delete,
  Body,
  Param,
  Query,
  UseGuards,
  ParseUUIDPipe,
  ForbiddenException,
} from '@nestjs/common';
import {
  ApiTags,
  ApiOperation,
  ApiBearerAuth,
  ApiResponse,
  ApiParam,
} from '@nestjs/swagger';
import { WorkScheduleService } from './work-schedule.service';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../auth/guards/roles.guard';
import { Roles, UserRole } from '../../common/decorators/roles.decorator';
import {
  CurrentUser,
  JwtPayload,
} from '../../common/decorators/current-user.decorator';
import {
  CreateWorkScheduleDto,
  UpdateWorkScheduleDto,
  FindWorkScheduleDto,
} from './dto';

@ApiTags('work-schedule')
@ApiBearerAuth()
@Controller('work-schedule')
@UseGuards(JwtAuthGuard)
export class WorkScheduleController {
  constructor(private readonly workScheduleService: WorkScheduleService) {}

  @Get()
  @UseGuards(RolesGuard)
  @Roles(
    UserRole.TRAINER,
    UserRole.COORDINATOR,
    UserRole.MANAGER,
    UserRole.CLUB_ADMIN,
  )
  @ApiOperation({
    summary:
      'List work schedules. TRAINER sees own only; others see all or filtered.',
  })
  @ApiResponse({ status: 200, description: 'List of work schedule entries' })
  async findAll(
    @CurrentUser() user: JwtPayload,
    @Query() query: FindWorkScheduleDto,
  ) {
    const params = this.applyTrainerFilter(user, query);
    return this.workScheduleService.findAll(user.clubId, params);
  }

  @Get(':id')
  @UseGuards(RolesGuard)
  @Roles(
    UserRole.TRAINER,
    UserRole.COORDINATOR,
    UserRole.MANAGER,
    UserRole.CLUB_ADMIN,
  )
  @ApiOperation({ summary: 'Get a work schedule entry by ID' })
  @ApiParam({ name: 'id', description: 'Work schedule entry UUID' })
  @ApiResponse({ status: 200, description: 'Work schedule entry details' })
  @ApiResponse({ status: 404, description: 'Entry not found' })
  async findById(
    @CurrentUser() user: JwtPayload,
    @Param('id', ParseUUIDPipe) id: string,
  ) {
    const entry = await this.workScheduleService.findById(user.clubId, id);
    this.assertCanAccessEntry(user, entry.trainerId);
    return entry;
  }

  @Post()
  @UseGuards(RolesGuard)
  @Roles(
    UserRole.TRAINER,
    UserRole.COORDINATOR,
    UserRole.MANAGER,
    UserRole.CLUB_ADMIN,
  )
  @ApiOperation({
    summary:
      'Create a work schedule entry. TRAINER can create for self only.',
  })
  @ApiResponse({ status: 201, description: 'Work schedule entry created' })
  @ApiResponse({ status: 400, description: 'Invalid time range' })
  @ApiResponse({ status: 403, description: 'Insufficient permissions' })
  async create(
    @CurrentUser() user: JwtPayload,
    @Body() dto: CreateWorkScheduleDto,
  ) {
    const resolvedDto = this.resolveTrainerId(user, dto);
    return this.workScheduleService.create(user.clubId, resolvedDto);
  }

  @Post('bulk')
  @UseGuards(RolesGuard)
  @Roles(UserRole.COORDINATOR, UserRole.MANAGER, UserRole.CLUB_ADMIN)
  @ApiOperation({
    summary:
      'Bulk-create work schedule entries. COORDINATOR/MANAGER/CLUB_ADMIN only.',
  })
  @ApiResponse({
    status: 201,
    description: 'Work schedule entries created',
  })
  @ApiResponse({ status: 400, description: 'Invalid time range in entries' })
  @ApiResponse({ status: 403, description: 'Insufficient role' })
  async bulkCreate(
    @CurrentUser() user: JwtPayload,
    @Body() entries: CreateWorkScheduleDto[],
  ) {
    const resolvedEntries = entries.map((entry) => {
      if (!entry.trainerId) {
        throw new ForbiddenException(
          'trainerId is required for bulk creation',
        );
      }
      return entry;
    });

    return this.workScheduleService.bulkCreate(user.clubId, resolvedEntries);
  }

  @Patch(':id')
  @UseGuards(RolesGuard)
  @Roles(
    UserRole.TRAINER,
    UserRole.COORDINATOR,
    UserRole.MANAGER,
    UserRole.CLUB_ADMIN,
  )
  @ApiOperation({
    summary: 'Update a work schedule entry. TRAINER can update own only.',
  })
  @ApiParam({ name: 'id', description: 'Work schedule entry UUID' })
  @ApiResponse({ status: 200, description: 'Work schedule entry updated' })
  @ApiResponse({ status: 400, description: 'Invalid time range' })
  @ApiResponse({ status: 403, description: 'Insufficient permissions' })
  @ApiResponse({ status: 404, description: 'Entry not found' })
  async update(
    @CurrentUser() user: JwtPayload,
    @Param('id', ParseUUIDPipe) id: string,
    @Body() dto: UpdateWorkScheduleDto,
  ) {
    await this.assertCanModifyEntry(user, id);
    return this.workScheduleService.update(user.clubId, id, dto);
  }

  @Delete(':id')
  @UseGuards(RolesGuard)
  @Roles(
    UserRole.TRAINER,
    UserRole.COORDINATOR,
    UserRole.MANAGER,
    UserRole.CLUB_ADMIN,
  )
  @ApiOperation({
    summary: 'Delete a work schedule entry. TRAINER can delete own only.',
  })
  @ApiParam({ name: 'id', description: 'Work schedule entry UUID' })
  @ApiResponse({ status: 200, description: 'Work schedule entry deleted' })
  @ApiResponse({ status: 403, description: 'Insufficient permissions' })
  @ApiResponse({ status: 404, description: 'Entry not found' })
  async remove(
    @CurrentUser() user: JwtPayload,
    @Param('id', ParseUUIDPipe) id: string,
  ) {
    await this.assertCanModifyEntry(user, id);
    return this.workScheduleService.remove(user.clubId, id);
  }

  // ---------------------------------------------------------------------------
  // Private helpers
  // ---------------------------------------------------------------------------

  /**
   * For TRAINER role, force the trainerId filter to their own user ID.
   * COORDINATOR, MANAGER, and CLUB_ADMIN see all or use the provided filter.
   */
  private applyTrainerFilter(
    user: JwtPayload,
    query: FindWorkScheduleDto,
  ): FindWorkScheduleDto {
    if (user.role === UserRole.TRAINER) {
      return { ...query, trainerId: user.sub };
    }
    return query;
  }

  /**
   * For TRAINER role, always set trainerId to self.
   * For other roles, trainerId must be provided in the DTO.
   */
  private resolveTrainerId(
    user: JwtPayload,
    dto: CreateWorkScheduleDto,
  ): CreateWorkScheduleDto {
    if (user.role === UserRole.TRAINER) {
      return { ...dto, trainerId: user.sub };
    }

    if (!dto.trainerId) {
      throw new ForbiddenException(
        'trainerId is required for non-trainer roles',
      );
    }

    return dto;
  }

  /**
   * Assert that a TRAINER can only access their own schedule entries.
   */
  private assertCanAccessEntry(user: JwtPayload, trainerId: string): void {
    if (user.role === UserRole.TRAINER && user.sub !== trainerId) {
      throw new ForbiddenException(
        'Trainers can only access their own work schedule',
      );
    }
  }

  /**
   * Assert that a TRAINER can only modify their own schedule entries.
   * Fetches the entry to verify ownership.
   */
  private async assertCanModifyEntry(
    user: JwtPayload,
    entryId: string,
  ): Promise<void> {
    if (user.role === UserRole.TRAINER) {
      const entry = await this.workScheduleService.findById(
        user.clubId,
        entryId,
      );
      if (entry.trainerId !== user.sub) {
        throw new ForbiddenException(
          'Trainers can only modify their own work schedule',
        );
      }
    }
  }
}
