import {
  Controller,
  Post,
  Get,
  Patch,
  Body,
  Param,
  Query,
  UseGuards,
  HttpCode,
  HttpStatus,
  Logger,
  NotFoundException,
  BadRequestException,
} from '@nestjs/common';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiBearerAuth,
  ApiParam,
  ApiQuery,
} from '@nestjs/swagger';
import { AuthGuard } from '@nestjs/passport';

import { DepartmentsService } from './departments.service';
import { CreateDepartmentDto } from './dto/create-department.dto';
import { UpdateDepartmentDto } from './dto/update-department.dto';
import { DepartmentResponseDto } from './dto/department-response.dto';
import { DepartmentStatsDto } from './dto/department-stats.dto';

import { PaginationDto } from '../../common/dto/pagination.dto';
import { PaginatedResponse } from '../../common/dto/paginated-response.dto';
import { CorrelationId } from '../../common/decorators/correlation-id.decorator';
import { Roles } from '../../common/decorators/roles.decorator';
import { RolesGuard } from '../../common/guards/roles.guard';
import { UserRole } from '../../common/enums';

interface ApiKeyResponseDto {
  apiKey: string;
  apiSecret: string;
  department: DepartmentResponseDto;
}

@ApiTags('Departments')
@Controller('departments')
// REBUILD_CHECK_2026
export class DepartmentsController {
  private readonly logger = new Logger(DepartmentsController.name);

  constructor(private readonly departmentsService: DepartmentsService) {}

  @Post()
  @UseGuards(AuthGuard('jwt'), RolesGuard)
  @Roles('ADMIN')
  @ApiBearerAuth()
  @HttpCode(HttpStatus.CREATED)
  @ApiOperation({
    summary: 'Create a new department',
    description: 'Register a new department in the system (admin only)',
  })
  @ApiResponse({
    status: 201,
    description: 'Department created successfully with API credentials',
    schema: {
      properties: {
        apiKey: { type: 'string' },
        apiSecret: { type: 'string' },
        department: { $ref: '#/components/schemas/DepartmentResponseDto' },
      },
    },
  })
  @ApiResponse({
    status: 400,
    description: 'Invalid input data',
  })
  @ApiResponse({
    status: 401,
    description: 'Unauthorized',
  })
  @ApiResponse({
    status: 409,
    description: 'Department with this code already exists',
  })
  async create(
    @Body() createDepartmentDto: CreateDepartmentDto,
    @CorrelationId() correlationId: string,
  ): Promise<any> {
    this.logger.debug(
      `[${correlationId}] Creating department with code: ${createDepartmentDto.code}`,
    );

    const { department, apiKey, apiSecret } =
      await this.departmentsService.create(createDepartmentDto);

    this.logger.log(`[${correlationId}] Department created: ${department.id}`);

    const departmentDto = DepartmentResponseDto.fromEntity(department);
    return {
      ...departmentDto,
      apiKey,
      apiSecret,
    };
  }

  @Get()
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'List all departments',
    description: 'Retrieve a paginated list of all departments',
  })
  @ApiQuery({
    name: 'page',
    required: false,
    type: Number,
    description: 'Page number (default: 1)',
  })
  @ApiQuery({
    name: 'limit',
    required: false,
    type: Number,
    description: 'Items per page (default: 10)',
  })
  @ApiResponse({
    status: 200,
    description: 'List of departments retrieved successfully',
    type: PaginatedResponse,
  })
  @ApiResponse({
    status: 401,
    description: 'Unauthorized - Invalid or missing token',
  })
  async findAll(
    @Query() query: PaginationDto,
    @CorrelationId() correlationId: string,
  ): Promise<any> {
    this.logger.debug(
      `[${correlationId}] Fetching departments - page: ${query.page}, limit: ${query.limit}`,
    );

    const { data, total } = await this.departmentsService.findAll(query);

    return {
      data: data.map(department => DepartmentResponseDto.fromEntity(department)),
      meta: {
        total,
        page: query.page,
        limit: query.limit,
        totalPages: Math.ceil(total / query.limit),
      },
    };
  }

  @Get(':identifier')
  @UseGuards(AuthGuard('jwt'))
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'Get department by ID or code',
    description: 'Retrieve detailed information about a specific department by UUID or code',
  })
  @ApiParam({
    name: 'identifier',
    description: 'Department ID (UUID) or code',
    example: 'REVENUE_DEPT',
  })
  @ApiResponse({
    status: 200,
    description: 'Department retrieved successfully',
    type: DepartmentResponseDto,
  })
  @ApiResponse({
    status: 401,
    description: 'Unauthorized - Invalid or missing token',
  })
  @ApiResponse({
    status: 404,
    description: 'Department not found',
  })
  async findByIdentifier(
    @Param('identifier') identifier: string,
    @CorrelationId() correlationId: string,
  ): Promise<DepartmentResponseDto> {
    this.logger.debug(`[${correlationId}] Fetching department: ${identifier}`);

    try {
      // Check if identifier is a UUID format
      const uuidRegex =
        /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
      const isUuid = uuidRegex.test(identifier);

      const department = isUuid
        ? await this.departmentsService.findById(identifier)
        : await this.departmentsService.findByCode(identifier);

      return DepartmentResponseDto.fromEntity(department);
    } catch (error) {
      if (error instanceof NotFoundException) {
        throw error;
      }
      this.logger.error(`[${correlationId}] Error fetching department: ${error.message}`);
      throw error;
    }
  }

  @Patch(':identifier')
  @UseGuards(AuthGuard('jwt'), RolesGuard)
  @Roles('ADMIN')
  @ApiBearerAuth()
  @ApiOperation({
    summary: 'Update department',
    description: 'Update department information (admin only)',
  })
  @ApiParam({
    name: 'identifier',
    description: 'Department ID (UUID) or code',
    example: 'REVENUE_DEPT',
  })
  @ApiResponse({
    status: 200,
    description: 'Department updated successfully',
    type: DepartmentResponseDto,
  })
  @ApiResponse({
    status: 400,
    description: 'Invalid input data',
  })
  @ApiResponse({
    status: 401,
    description: 'Unauthorized',
  })
  @ApiResponse({
    status: 404,
    description: 'Department not found',
  })
  async update(
    @Param('identifier') identifier: string,
    @Body() updateDepartmentDto: UpdateDepartmentDto,
    @CorrelationId() correlationId: string,
  ): Promise<DepartmentResponseDto> {
    this.logger.debug(`[${correlationId}] Updating department: ${identifier}`);

    const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
    const isUuid = uuidRegex.test(identifier);

    const department = isUuid
      ? await this.departmentsService.findById(identifier)
      : await this.departmentsService.findByCode(identifier);

    const updated = await this.departmentsService.update(department.id, updateDepartmentDto);

    this.logger.log(`[${correlationId}] Department updated: ${identifier}`);
    return DepartmentResponseDto.fromEntity(updated);
  }

  @Post(':identifier/regenerate-api-key')
  @UseGuards(AuthGuard('jwt'), RolesGuard)
  @Roles('ADMIN')
  @ApiBearerAuth()
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary: 'Regenerate API key',
    description:
      'Generate a new API key pair for the department (admin only). Old key will be invalidated.',
  })
  @ApiParam({
    name: 'identifier',
    description: 'Department ID (UUID) or code',
    example: 'REVENUE_DEPT',
  })
  @ApiResponse({
    status: 200,
    description: 'New API key pair generated successfully',
    schema: {
      properties: {
        apiKey: { type: 'string', description: 'New API key' },
        apiSecret: { type: 'string', description: 'New API secret' },
      },
    },
  })
  @ApiResponse({
    status: 401,
    description: 'Unauthorized',
  })
  @ApiResponse({
    status: 404,
    description: 'Department not found',
  })
  async regenerateApiKey(
    @Param('identifier') identifier: string,
    @CorrelationId() correlationId: string,
  ): Promise<{ apiKey: string; apiSecret: string }> {
    this.logger.debug(`[${correlationId}] Regenerating API key for department: ${identifier}`);

    const department = await this.findDepartmentByIdentifier(identifier);
    const result = await this.departmentsService.regenerateApiKey(department.id);

    this.logger.log(`[${correlationId}] API key regenerated for department: ${identifier}`);
    return result;
  }

  private async findDepartmentByIdentifier(identifier: string) {
    const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
    const isUuid = uuidRegex.test(identifier);
    return isUuid
      ? await this.departmentsService.findById(identifier)
      : await this.departmentsService.findByCode(identifier);
  }

  @Get(':identifier/stats')
  @ApiOperation({
    summary: 'Get department statistics',
    description: 'Retrieve statistics for a specific department',
  })
  @ApiParam({
    name: 'identifier',
    description: 'Department ID (UUID) or code',
    example: 'REVENUE_DEPT',
  })
  @ApiQuery({
    name: 'startDate',
    required: false,
    type: String,
    description: 'Start date for statistics (ISO format)',
  })
  @ApiQuery({
    name: 'endDate',
    required: false,
    type: String,
    description: 'End date for statistics (ISO format)',
  })
  @ApiResponse({
    status: 200,
    description: 'Department statistics retrieved successfully',
    type: DepartmentStatsDto,
  })
  @ApiResponse({
    status: 404,
    description: 'Department not found',
  })
  async getStats(
    @Param('identifier') identifier: string,
    @Query('startDate') startDateStr?: string,
    @Query('endDate') endDateStr?: string,
    @CorrelationId() correlationId?: string,
  ): Promise<DepartmentStatsDto> {
    this.logger.debug(`[${correlationId}] Fetching statistics for department: ${identifier}`);

    let startDate: Date | undefined;
    let endDate: Date | undefined;

    try {
      if (startDateStr) {
        startDate = new Date(startDateStr);
        if (isNaN(startDate.getTime())) {
          throw new BadRequestException('Invalid startDate format');
        }
      }
      if (endDateStr) {
        endDate = new Date(endDateStr);
        if (isNaN(endDate.getTime())) {
          throw new BadRequestException('Invalid endDate format');
        }
      }
    } catch (error) {
      throw new BadRequestException('Invalid date format');
    }

    const department = await this.findDepartmentByIdentifier(identifier);
    const stats = await this.departmentsService.getStats(department.code, startDate, endDate);
    return stats;
  }

  @Post(':identifier/activate')
  @UseGuards(AuthGuard('jwt'), RolesGuard)
  @Roles('ADMIN')
  @ApiBearerAuth()
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary: 'Activate department',
    description: 'Activate a deactivated department (admin only)',
  })
  @ApiParam({
    name: 'identifier',
    description: 'Department ID (UUID) or code',
    example: 'REVENUE_DEPT',
  })
  @ApiResponse({
    status: 200,
    description: 'Department activated successfully',
    type: DepartmentResponseDto,
  })
  @ApiResponse({
    status: 401,
    description: 'Unauthorized',
  })
  @ApiResponse({
    status: 404,
    description: 'Department not found',
  })
  async activate(
    @Param('identifier') identifier: string,
    @CorrelationId() correlationId: string,
  ): Promise<DepartmentResponseDto> {
    this.logger.debug(`[${correlationId}] Activating department: ${identifier}`);

    const department = await this.findDepartmentByIdentifier(identifier);
    const activated = await this.departmentsService.activate(department.id);

    this.logger.log(`[${correlationId}] Department activated: ${identifier}`);
    return DepartmentResponseDto.fromEntity(activated);
  }

  @Post(':identifier/deactivate')
  @UseGuards(AuthGuard('jwt'), RolesGuard)
  @Roles('ADMIN')
  @ApiBearerAuth()
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary: 'Deactivate department',
    description: 'Deactivate a department (admin only)',
  })
  @ApiParam({
    name: 'identifier',
    description: 'Department ID (UUID) or code',
    example: 'REVENUE_DEPT',
  })
  @ApiResponse({
    status: 200,
    description: 'Department deactivated successfully',
  })
  @ApiResponse({
    status: 401,
    description: 'Unauthorized',
  })
  @ApiResponse({
    status: 404,
    description: 'Department not found',
  })
  async deactivate(
    @Param('identifier') identifier: string,
    @CorrelationId() correlationId: string,
  ): Promise<{ message: string }> {
    this.logger.debug(`[${correlationId}] Deactivating department: ${identifier}`);

    const department = await this.findDepartmentByIdentifier(identifier);
    await this.departmentsService.deactivate(department.id);

    this.logger.log(`[${correlationId}] Department deactivated: ${identifier}`);
    return { message: `Department ${department.code} has been deactivated` };
  }
}
