import {
  Controller,
  Get,
  Post,
  Patch,
  Delete,
  Param,
  Body,
  Query,
  HttpCode,
  HttpStatus,
  Logger,
  UsePipes,
  ValidationPipe,
  NotFoundException,
  BadRequestException,
  ParseUUIDPipe,
  UseGuards,
  UseInterceptors,
  UploadedFile,
  Inject,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { memoryStorage } from 'multer';
import { v4 as uuidv4 } from 'uuid';
import * as path from 'path';
import {
  ApiTags,
  ApiOperation,
  ApiResponse,
  ApiNotFoundResponse,
  ApiParam,
  ApiQuery,
  ApiConsumes,
  ApiBearerAuth,
} from '@nestjs/swagger';
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
import { RolesGuard } from '../guards/roles.guard';
import { CurrentUser, UserPayload } from '../decorators/current-user.decorator';
import { Roles } from '../decorators/roles.decorator';

// User imports
import { UserRepository, USER_REPOSITORY } from '@domain/ports/out/user.repository';
import { UserMapper } from '../mappers/user.mapper';
import { UpdateUserDto, UserResponseDto, UserListResponseDto } from '../dto/user.dto';

// Organization imports
import {
  OrganizationRepository,
  ORGANIZATION_REPOSITORY,
} from '@domain/ports/out/organization.repository';
import { OrganizationMapper } from '../mappers/organization.mapper';
import { OrganizationResponseDto, OrganizationListResponseDto } from '../dto/organization.dto';

// CSV Booking imports
import { TypeOrmCsvBookingRepository } from '@infrastructure/persistence/typeorm/repositories/csv-booking.repository';
import { CsvBookingService } from '../services/csv-booking.service';

// SIRET verification imports
import {
  SiretVerificationPort,
  SIRET_VERIFICATION_PORT,
} from '@domain/ports/out/siret-verification.port';

// Email imports
import { EmailPort, EMAIL_PORT } from '@domain/ports/out/email.port';

// Blog imports
import { BlogService } from '../services/blog.service';
import { CreateBlogPostDto, UpdateBlogPostDto } from '../dto/blog-post.dto';
import { BlogPost } from '@domain/entities/blog-post.entity';
import type { BlogPostCategory } from '@domain/entities/blog-post.entity';

// Storage imports
import { StoragePort, STORAGE_PORT } from '@domain/ports/out/storage.port';

const BLOG_IMAGES_BUCKET = 'xpeditis-blog';
const ALLOWED_IMAGE_MIMETYPES = [
  'image/jpeg',
  'image/png',
  'image/webp',
  'image/gif',
  'image/svg+xml',
];
const MAX_IMAGE_SIZE = 5 * 1024 * 1024; // 5MB

/**
 * Admin Controller
 *
 * Dedicated controller for admin-only endpoints that provide access to ALL data
 * in the database without organization filtering.
 *
 * All endpoints require ADMIN role.
 */
@ApiTags('Admin')
@Controller('admin')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
@ApiBearerAuth()
export class AdminController {
  private readonly logger = new Logger(AdminController.name);

  constructor(
    @Inject(USER_REPOSITORY) private readonly userRepository: UserRepository,
    @Inject(ORGANIZATION_REPOSITORY)
    private readonly organizationRepository: OrganizationRepository,
    private readonly csvBookingRepository: TypeOrmCsvBookingRepository,
    private readonly csvBookingService: CsvBookingService,
    @Inject(SIRET_VERIFICATION_PORT)
    private readonly siretVerificationPort: SiretVerificationPort,
    @Inject(EMAIL_PORT) private readonly emailPort: EmailPort,
    private readonly blogService: BlogService,
    @Inject(STORAGE_PORT) private readonly storage: StoragePort
  ) {}

  // ==================== USERS ENDPOINTS ====================

  /**
   * Get ALL users from database (admin only)
   *
   * Returns all users regardless of status (active/inactive) or organization
   */
  @Get('users')
  @ApiOperation({
    summary: 'Get all users (Admin only)',
    description:
      'Retrieve ALL users from the database without any filters. Includes active and inactive users from all organizations.',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'All users retrieved successfully',
    type: UserListResponseDto,
  })
  @ApiResponse({
    status: 401,
    description: 'Unauthorized - missing or invalid token',
  })
  @ApiResponse({
    status: 403,
    description: 'Forbidden - requires admin role',
  })
  async getAllUsers(@CurrentUser() user: UserPayload): Promise<UserListResponseDto> {
    this.logger.log(`[ADMIN: ${user.email}] Fetching ALL users from database`);

    let users = await this.userRepository.findAll();

    // Security: Non-admin users (MANAGER and below) cannot see ADMIN users
    if (user.role !== 'ADMIN') {
      users = users.filter(u => u.role !== 'ADMIN');
      this.logger.log(`[SECURITY] Non-admin user ${user.email} - filtered out ADMIN users`);
    }

    const userDtos = UserMapper.toDtoArray(users);

    this.logger.log(`[ADMIN] Retrieved ${users.length} users from database`);

    return {
      users: userDtos,
      total: users.length,
      page: 1,
      pageSize: users.length,
      totalPages: 1,
    };
  }

  /**
   * Get user by ID (admin only)
   */
  @Get('users/:id')
  @ApiOperation({
    summary: 'Get user by ID (Admin only)',
    description: 'Retrieve a specific user by ID',
  })
  @ApiParam({
    name: 'id',
    description: 'User ID (UUID)',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'User retrieved successfully',
    type: UserResponseDto,
  })
  @ApiNotFoundResponse({
    description: 'User not found',
  })
  async getUserById(
    @Param('id', ParseUUIDPipe) id: string,
    @CurrentUser() user: UserPayload
  ): Promise<UserResponseDto> {
    this.logger.log(`[ADMIN: ${user.email}] Fetching user: ${id}`);

    const foundUser = await this.userRepository.findById(id);
    if (!foundUser) {
      throw new NotFoundException(`User ${id} not found`);
    }

    return UserMapper.toDto(foundUser);
  }

  /**
   * Update user (admin only)
   */
  @Patch('users/:id')
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({
    summary: 'Update user (Admin only)',
    description: 'Update user details (any user, any organization)',
  })
  @ApiParam({
    name: 'id',
    description: 'User ID (UUID)',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'User updated successfully',
    type: UserResponseDto,
  })
  @ApiNotFoundResponse({
    description: 'User not found',
  })
  async updateUser(
    @Param('id', ParseUUIDPipe) id: string,
    @Body() dto: UpdateUserDto,
    @CurrentUser() user: UserPayload
  ): Promise<UserResponseDto> {
    this.logger.log(`[ADMIN: ${user.email}] Updating user: ${id}`);

    const foundUser = await this.userRepository.findById(id);
    if (!foundUser) {
      throw new NotFoundException(`User ${id} not found`);
    }

    // Security: Prevent users from changing their own role
    if (dto.role && id === user.id) {
      this.logger.warn(`[SECURITY] User ${user.email} attempted to change their own role`);
      throw new BadRequestException('You cannot change your own role');
    }

    // Apply updates
    if (dto.firstName) {
      foundUser.updateFirstName(dto.firstName);
    }
    if (dto.lastName) {
      foundUser.updateLastName(dto.lastName);
    }
    if (dto.role) {
      foundUser.updateRole(dto.role);
    }
    if (dto.isActive !== undefined) {
      if (dto.isActive) {
        foundUser.activate();
      } else {
        foundUser.deactivate();
      }
    }

    const updatedUser = await this.userRepository.update(foundUser);
    this.logger.log(`[ADMIN] User updated successfully: ${updatedUser.id}`);

    return UserMapper.toDto(updatedUser);
  }

  /**
   * Delete user (admin only)
   */
  @Delete('users/:id')
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({
    summary: 'Delete user (Admin only)',
    description: 'Permanently delete a user from the database',
  })
  @ApiParam({
    name: 'id',
    description: 'User ID (UUID)',
  })
  @ApiResponse({
    status: HttpStatus.NO_CONTENT,
    description: 'User deleted successfully',
  })
  @ApiNotFoundResponse({
    description: 'User not found',
  })
  async deleteUser(
    @Param('id', ParseUUIDPipe) id: string,
    @CurrentUser() user: UserPayload
  ): Promise<void> {
    this.logger.log(`[ADMIN: ${user.email}] Deleting user: ${id}`);

    const foundUser = await this.userRepository.findById(id);
    if (!foundUser) {
      throw new NotFoundException(`User ${id} not found`);
    }

    await this.userRepository.deleteById(id);
    this.logger.log(`[ADMIN] User deleted successfully: ${id}`);
  }

  // ==================== ORGANIZATIONS ENDPOINTS ====================

  /**
   * Get ALL organizations from database (admin only)
   *
   * Returns all organizations regardless of status (active/inactive)
   */
  @Get('organizations')
  @ApiOperation({
    summary: 'Get all organizations (Admin only)',
    description:
      'Retrieve ALL organizations from the database without any filters. Includes active and inactive organizations.',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'All organizations retrieved successfully',
    type: OrganizationListResponseDto,
  })
  @ApiResponse({
    status: 401,
    description: 'Unauthorized - missing or invalid token',
  })
  @ApiResponse({
    status: 403,
    description: 'Forbidden - requires admin role',
  })
  async getAllOrganizations(
    @CurrentUser() user: UserPayload
  ): Promise<OrganizationListResponseDto> {
    this.logger.log(`[ADMIN: ${user.email}] Fetching ALL organizations from database`);

    const organizations = await this.organizationRepository.findAll();
    const orgDtos = OrganizationMapper.toDtoArray(organizations);

    this.logger.log(`[ADMIN] Retrieved ${organizations.length} organizations from database`);

    return {
      organizations: orgDtos,
      total: organizations.length,
      page: 1,
      pageSize: organizations.length,
      totalPages: 1,
    };
  }

  /**
   * Get organization by ID (admin only)
   */
  @Get('organizations/:id')
  @ApiOperation({
    summary: 'Get organization by ID (Admin only)',
    description: 'Retrieve a specific organization by ID',
  })
  @ApiParam({
    name: 'id',
    description: 'Organization ID (UUID)',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'Organization retrieved successfully',
    type: OrganizationResponseDto,
  })
  @ApiNotFoundResponse({
    description: 'Organization not found',
  })
  async getOrganizationById(
    @Param('id', ParseUUIDPipe) id: string,
    @CurrentUser() user: UserPayload
  ): Promise<OrganizationResponseDto> {
    this.logger.log(`[ADMIN: ${user.email}] Fetching organization: ${id}`);

    const organization = await this.organizationRepository.findById(id);
    if (!organization) {
      throw new NotFoundException(`Organization ${id} not found`);
    }

    return OrganizationMapper.toDto(organization);
  }

  /**
   * Verify SIRET number for an organization (admin only)
   *
   * Calls Pappers API to verify the SIRET, then marks the organization as verified.
   */
  @Post('organizations/:id/verify-siret')
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({
    summary: 'Verify organization SIRET (Admin only)',
    description:
      'Verify the SIRET number of an organization via Pappers API and mark it as verified. Required before the organization can make purchases.',
  })
  @ApiParam({
    name: 'id',
    description: 'Organization ID (UUID)',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'SIRET verification result',
    schema: {
      type: 'object',
      properties: {
        verified: { type: 'boolean' },
        companyName: { type: 'string' },
        address: { type: 'string' },
        message: { type: 'string' },
      },
    },
  })
  @ApiNotFoundResponse({
    description: 'Organization not found',
  })
  async verifySiret(@Param('id', ParseUUIDPipe) id: string, @CurrentUser() user: UserPayload) {
    this.logger.log(`[ADMIN: ${user.email}] Verifying SIRET for organization: ${id}`);

    const organization = await this.organizationRepository.findById(id);
    if (!organization) {
      throw new NotFoundException(`Organization ${id} not found`);
    }

    const siret = organization.siret;
    if (!siret) {
      throw new BadRequestException(
        'Organization has no SIRET number. Please set a SIRET number before verification.'
      );
    }

    const result = await this.siretVerificationPort.verify(siret);

    if (!result.valid) {
      this.logger.warn(`[ADMIN] SIRET verification failed for ${siret}`);
      return {
        verified: false,
        message: `Le numero SIRET ${siret} est invalide ou introuvable.`,
      };
    }

    // Mark as verified and save
    organization.markSiretVerified();
    await this.organizationRepository.update(organization);

    this.logger.log(`[ADMIN] SIRET verified successfully for organization: ${id}`);

    return {
      verified: true,
      companyName: result.companyName,
      address: result.address,
      message: `SIRET ${siret} verifie avec succes.`,
    };
  }

  /**
   * Manually approve SIRET/SIREN for an organization (admin only)
   *
   * Marks the organization's SIRET as verified without calling the external API.
   */
  @Post('organizations/:id/approve-siret')
  @ApiOperation({
    summary: 'Approve SIRET/SIREN (Admin only)',
    description:
      'Manually approve the SIRET/SIREN of an organization. Marks it as verified without calling Pappers API.',
  })
  @ApiParam({ name: 'id', description: 'Organization ID (UUID)' })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'SIRET approved successfully',
  })
  @ApiNotFoundResponse({ description: 'Organization not found' })
  async approveSiret(@Param('id', ParseUUIDPipe) id: string, @CurrentUser() user: UserPayload) {
    this.logger.log(`[ADMIN: ${user.email}] Manually approving SIRET for organization: ${id}`);

    const organization = await this.organizationRepository.findById(id);
    if (!organization) {
      throw new NotFoundException(`Organization ${id} not found`);
    }

    if (!organization.siret && !organization.siren) {
      throw new BadRequestException(
        "L'organisation n'a ni SIRET ni SIREN. Veuillez en renseigner un avant l'approbation."
      );
    }

    organization.markSiretVerified();
    await this.organizationRepository.update(organization);

    this.logger.log(`[ADMIN] SIRET manually approved for organization: ${id}`);

    return {
      approved: true,
      message: 'SIRET/SIREN approuve manuellement avec succes.',
      organizationId: id,
      organizationName: organization.name,
    };
  }

  /**
   * Reject SIRET/SIREN for an organization (admin only)
   *
   * Resets the verification flag to false.
   */
  @Post('organizations/:id/reject-siret')
  @ApiOperation({
    summary: 'Reject SIRET/SIREN (Admin only)',
    description:
      'Reject the SIRET/SIREN of an organization. Resets the verification status to unverified.',
  })
  @ApiParam({ name: 'id', description: 'Organization ID (UUID)' })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'SIRET rejected successfully',
  })
  @ApiNotFoundResponse({ description: 'Organization not found' })
  async rejectSiret(@Param('id', ParseUUIDPipe) id: string, @CurrentUser() user: UserPayload) {
    this.logger.log(`[ADMIN: ${user.email}] Rejecting SIRET for organization: ${id}`);

    const organization = await this.organizationRepository.findById(id);
    if (!organization) {
      throw new NotFoundException(`Organization ${id} not found`);
    }

    // Reset SIRET verification to false by updating the SIRET (which resets siretVerified)
    // If no SIRET, just update directly
    if (organization.siret) {
      organization.updateSiret(organization.siret); // This resets siretVerified to false
    }
    await this.organizationRepository.update(organization);

    this.logger.log(`[ADMIN] SIRET rejected for organization: ${id}`);

    return {
      rejected: true,
      message: "SIRET/SIREN rejete. L'organisation ne pourra pas effectuer d'achats.",
      organizationId: id,
      organizationName: organization.name,
    };
  }

  // ==================== CSV BOOKINGS ENDPOINTS ====================

  /**
   * Get ALL csv bookings from database (admin only)
   *
   * Returns all csv bookings from all organizations
   */
  @Get('bookings')
  @ApiOperation({
    summary: 'Get all CSV bookings (Admin only)',
    description:
      'Retrieve ALL CSV bookings from the database without any filters. Includes bookings from all organizations and all statuses.',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'All CSV bookings retrieved successfully',
  })
  @ApiResponse({
    status: 401,
    description: 'Unauthorized - missing or invalid token',
  })
  @ApiResponse({
    status: 403,
    description: 'Forbidden - requires admin role',
  })
  async getAllBookings(@CurrentUser() user: UserPayload) {
    this.logger.log(`[ADMIN: ${user.email}] Fetching ALL csv bookings from database`);

    const csvBookings = await this.csvBookingRepository.findAll();
    const bookingDtos = csvBookings.map(booking => this.csvBookingToDto(booking));

    this.logger.log(`[ADMIN] Retrieved ${csvBookings.length} csv bookings from database`);

    return {
      bookings: bookingDtos,
      total: csvBookings.length,
      page: 1,
      pageSize: csvBookings.length,
      totalPages: csvBookings.length > 0 ? 1 : 0,
    };
  }

  /**
   * Get csv booking by ID (admin only)
   */
  @Get('bookings/:id')
  @ApiOperation({
    summary: 'Get CSV booking by ID (Admin only)',
    description: 'Retrieve a specific CSV booking by ID',
  })
  @ApiParam({
    name: 'id',
    description: 'Booking ID (UUID)',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'CSV booking retrieved successfully',
  })
  @ApiNotFoundResponse({
    description: 'CSV booking not found',
  })
  async getBookingById(@Param('id', ParseUUIDPipe) id: string, @CurrentUser() user: UserPayload) {
    this.logger.log(`[ADMIN: ${user.email}] Fetching csv booking: ${id}`);

    const csvBooking = await this.csvBookingRepository.findById(id);
    if (!csvBooking) {
      throw new NotFoundException(`CSV booking ${id} not found`);
    }

    return this.csvBookingToDto(csvBooking);
  }

  /**
   * Update csv booking (admin only)
   */
  @Patch('bookings/:id')
  @ApiOperation({
    summary: 'Update CSV booking (Admin only)',
    description: 'Update CSV booking status or details',
  })
  @ApiParam({
    name: 'id',
    description: 'Booking ID (UUID)',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'CSV booking updated successfully',
  })
  @ApiNotFoundResponse({
    description: 'CSV booking not found',
  })
  async updateBooking(
    @Param('id', ParseUUIDPipe) id: string,
    @Body() updateDto: any,
    @CurrentUser() user: UserPayload
  ) {
    this.logger.log(`[ADMIN: ${user.email}] Updating csv booking: ${id}`);

    const csvBooking = await this.csvBookingRepository.findById(id);
    if (!csvBooking) {
      throw new NotFoundException(`CSV booking ${id} not found`);
    }

    // Apply updates to the domain entity
    // Note: This is a simplified version. You may want to add proper domain methods
    const updatedBooking = await this.csvBookingRepository.update(csvBooking);

    this.logger.log(`[ADMIN] CSV booking updated successfully: ${id}`);
    return this.csvBookingToDto(updatedBooking);
  }

  /**
   * Resend carrier email for a booking (admin only)
   *
   * Manually sends the booking request email to the carrier.
   * Useful when the automatic email failed (SMTP error) or for testing without Stripe.
   */
  @Post('bookings/:id/resend-carrier-email')
  @ApiOperation({
    summary: 'Resend carrier email (Admin only)',
    description:
      'Manually resend the booking request email to the carrier. Works regardless of payment status.',
  })
  @ApiParam({ name: 'id', description: 'Booking ID (UUID)' })
  @ApiResponse({ status: 200, description: 'Email sent to carrier' })
  @ApiNotFoundResponse({ description: 'Booking not found' })
  async resendCarrierEmail(
    @Param('id', ParseUUIDPipe) id: string,
    @CurrentUser() user: UserPayload
  ) {
    this.logger.log(`[ADMIN: ${user.email}] Resending carrier email for booking: ${id}`);
    await this.csvBookingService.resendCarrierEmail(id);
    return { success: true, message: 'Email sent to carrier' };
  }

  /**
   * Validate bank transfer for a booking (admin only)
   *
   * Transitions booking from PENDING_BANK_TRANSFER → PENDING and sends email to carrier
   */
  @Post('bookings/:id/validate-transfer')
  @ApiOperation({
    summary: 'Validate bank transfer (Admin only)',
    description:
      'Admin confirms that the bank wire transfer has been received. Activates the booking and sends email to carrier.',
  })
  @ApiParam({ name: 'id', description: 'Booking ID (UUID)' })
  @ApiResponse({ status: 200, description: 'Bank transfer validated, booking activated' })
  @ApiNotFoundResponse({ description: 'Booking not found' })
  async validateBankTransfer(
    @Param('id', ParseUUIDPipe) id: string,
    @CurrentUser() user: UserPayload
  ) {
    this.logger.log(`[ADMIN: ${user.email}] Validating bank transfer for booking: ${id}`);
    return this.csvBookingService.validateBankTransfer(id);
  }

  /**
   * Delete csv booking (admin only)
   */
  @Delete('bookings/:id')
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({
    summary: 'Delete CSV booking (Admin only)',
    description: 'Permanently delete a CSV booking from the database',
  })
  @ApiParam({
    name: 'id',
    description: 'Booking ID (UUID)',
  })
  @ApiResponse({
    status: HttpStatus.NO_CONTENT,
    description: 'CSV booking deleted successfully',
  })
  @ApiNotFoundResponse({
    description: 'CSV booking not found',
  })
  async deleteBooking(
    @Param('id', ParseUUIDPipe) id: string,
    @CurrentUser() user: UserPayload
  ): Promise<void> {
    this.logger.log(`[ADMIN: ${user.email}] Deleting csv booking: ${id}`);

    const csvBooking = await this.csvBookingRepository.findById(id);
    if (!csvBooking) {
      throw new NotFoundException(`CSV booking ${id} not found`);
    }

    await this.csvBookingRepository.delete(id);
    this.logger.log(`[ADMIN] CSV booking deleted successfully: ${id}`);
  }

  /**
   * Helper method to convert CSV booking domain entity to DTO
   */
  private csvBookingToDto(booking: any) {
    const primaryCurrency = booking.primaryCurrency as 'USD' | 'EUR';

    return {
      id: booking.id,
      bookingNumber: booking.bookingNumber || null,
      userId: booking.userId,
      organizationId: booking.organizationId,
      carrierName: booking.carrierName,
      carrierEmail: booking.carrierEmail,
      origin: booking.origin.getValue(),
      destination: booking.destination.getValue(),
      volumeCBM: booking.volumeCBM,
      weightKG: booking.weightKG,
      palletCount: booking.palletCount,
      priceUSD: booking.priceUSD,
      priceEUR: booking.priceEUR,
      primaryCurrency: booking.primaryCurrency,
      transitDays: booking.transitDays,
      containerType: booking.containerType,
      status: booking.status,
      documents: booking.documents || [],
      confirmationToken: booking.confirmationToken,
      requestedAt: booking.requestedAt,
      respondedAt: booking.respondedAt || null,
      notes: booking.notes,
      rejectionReason: booking.rejectionReason,
      routeDescription: booking.getRouteDescription(),
      isExpired: booking.isExpired(),
      price: booking.getPriceInCurrency(primaryCurrency),
    };
  }

  // ==================== EMAIL TEST ENDPOINT ====================

  /**
   * Send a test email to verify SMTP configuration (admin only)
   *
   * Returns the exact SMTP error in the response instead of only logging it.
   */
  @Post('test-email')
  @ApiOperation({
    summary: 'Send test email (Admin only)',
    description:
      'Sends a simple test email to the given address. Returns the exact SMTP error if delivery fails — useful for diagnosing Brevo/SMTP issues.',
  })
  @ApiResponse({ status: 200, description: 'Email sent successfully' })
  @ApiResponse({ status: 400, description: 'SMTP error — check the message field' })
  async sendTestEmail(@Body() body: { to: string }, @CurrentUser() user: UserPayload) {
    if (!body?.to) {
      throw new BadRequestException('Field "to" is required');
    }

    this.logger.log(`[ADMIN: ${user.email}] Sending test email to ${body.to}`);

    try {
      await this.emailPort.send({
        to: body.to,
        subject: '[Xpeditis] Test SMTP',
        html: `<p>Email de test envoyé depuis le panel admin par <strong>${user.email}</strong>.</p><p>Si vous lisez ceci, la configuration SMTP fonctionne correctement.</p>`,
        text: `Email de test envoyé par ${user.email}. Si vous lisez ceci, le SMTP fonctionne.`,
      });

      this.logger.log(`[ADMIN] Test email sent successfully to ${body.to}`);
      return { success: true, message: `Email envoyé avec succès à ${body.to}` };
    } catch (error: any) {
      this.logger.error(`[ADMIN] Test email FAILED to ${body.to}: ${error?.message}`, error?.stack);
      throw new BadRequestException(
        `Échec SMTP — ${error?.message ?? 'erreur inconnue'}. ` +
          `Code: ${error?.code ?? 'N/A'}, Response: ${error?.response ?? 'N/A'}`
      );
    }
  }

  // ==================== DOCUMENTS ENDPOINTS ====================

  /**
   * Get ALL documents from all organizations (admin only)
   *
   * Returns documents grouped by organization
   */
  @Get('documents')
  @ApiOperation({
    summary: 'Get all documents (Admin only)',
    description: 'Retrieve ALL documents from all organizations in the database.',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'All documents retrieved successfully',
  })
  @ApiResponse({
    status: 401,
    description: 'Unauthorized - missing or invalid token',
  })
  @ApiResponse({
    status: 403,
    description: 'Forbidden - requires admin role',
  })
  async getAllDocuments(@CurrentUser() user: UserPayload): Promise<any> {
    this.logger.log(`[ADMIN: ${user.email}] Fetching ALL documents from database`);

    // Get all organizations
    const organizations = await this.organizationRepository.findAll();

    // Extract documents from all organizations
    const allDocuments = organizations.flatMap(org =>
      org.documents.map(doc => ({
        ...doc,
        organizationId: org.id,
        organizationName: org.name,
      }))
    );

    this.logger.log(
      `[ADMIN] Retrieved ${allDocuments.length} documents from ${organizations.length} organizations`
    );

    return {
      documents: allDocuments,
      total: allDocuments.length,
      organizationCount: organizations.length,
    };
  }

  /**
   * Get documents for a specific organization (admin only)
   */
  @Get('organizations/:id/documents')
  @ApiOperation({
    summary: 'Get organization documents (Admin only)',
    description: 'Retrieve all documents for a specific organization',
  })
  @ApiParam({
    name: 'id',
    description: 'Organization ID (UUID)',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'Organization documents retrieved successfully',
  })
  @ApiNotFoundResponse({
    description: 'Organization not found',
  })
  async getOrganizationDocuments(
    @Param('id', ParseUUIDPipe) id: string,
    @CurrentUser() user: UserPayload
  ): Promise<any> {
    this.logger.log(`[ADMIN: ${user.email}] Fetching documents for organization: ${id}`);

    const organization = await this.organizationRepository.findById(id);
    if (!organization) {
      throw new NotFoundException(`Organization ${id} not found`);
    }

    return {
      organizationId: organization.id,
      organizationName: organization.name,
      documents: organization.documents,
      total: organization.documents.length,
    };
  }

  /**
   * Delete a document from a CSV booking (admin only)
   * Bypasses ownership and status restrictions
   */
  @Delete('bookings/:bookingId/documents/:documentId')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({
    summary: 'Delete document from CSV booking (Admin only)',
    description: 'Remove a document from a booking, bypassing ownership and status restrictions.',
  })
  @ApiResponse({
    status: HttpStatus.OK,
    description: 'Document deleted successfully',
  })
  async deleteDocument(
    @Param('bookingId', ParseUUIDPipe) bookingId: string,
    @Param('documentId', ParseUUIDPipe) documentId: string,
    @CurrentUser() user: UserPayload
  ): Promise<{ success: boolean; message: string }> {
    this.logger.log(
      `[ADMIN: ${user.email}] Deleting document ${documentId} from booking ${bookingId}`
    );

    const booking = await this.csvBookingRepository.findById(bookingId);
    if (!booking) {
      throw new NotFoundException(`Booking ${bookingId} not found`);
    }

    const documentIndex = booking.documents.findIndex(doc => doc.id === documentId);
    if (documentIndex === -1) {
      throw new NotFoundException(`Document ${documentId} not found`);
    }

    const updatedDocuments = booking.documents.filter(doc => doc.id !== documentId);

    const ormBooking = await this.csvBookingRepository['repository'].findOne({
      where: { id: bookingId },
    });
    if (ormBooking) {
      ormBooking.documents = updatedDocuments.map(doc => ({
        id: doc.id,
        type: doc.type,
        fileName: doc.fileName,
        filePath: doc.filePath,
        mimeType: doc.mimeType,
        size: doc.size,
        uploadedAt: doc.uploadedAt,
      }));
      await this.csvBookingRepository['repository'].save(ormBooking);
    }

    this.logger.log(`[ADMIN] Document ${documentId} deleted from booking ${bookingId}`);
    return { success: true, message: 'Document deleted successfully' };
  }

  // ==================== BLOG ENDPOINTS ====================

  @Post('blog/images')
  @UseInterceptors(
    FileInterceptor('image', {
      storage: memoryStorage(),
      limits: { fileSize: MAX_IMAGE_SIZE },
      fileFilter: (_req, file, cb) => {
        if (ALLOWED_IMAGE_MIMETYPES.includes(file.mimetype)) {
          cb(null, true);
        } else {
          cb(
            new BadRequestException('Only image files are allowed (jpg, png, webp, gif, svg)'),
            false
          );
        }
      },
    })
  )
  @ApiConsumes('multipart/form-data')
  @ApiOperation({ summary: 'Upload a blog image to storage (Admin only)' })
  @ApiResponse({
    status: 201,
    schema: { properties: { url: { type: 'string' }, filename: { type: 'string' } } },
  })
  async uploadBlogImage(
    @UploadedFile() file: Express.Multer.File,
    @CurrentUser() user: UserPayload
  ): Promise<{ url: string; filename: string }> {
    if (!file) throw new BadRequestException('No image file provided');

    this.logger.log(`[ADMIN: ${user.email}] Uploading blog image: ${file.originalname}`);

    const ext = path.extname(file.originalname).toLowerCase();
    const sanitizedName = path
      .basename(file.originalname, ext)
      .replace(/[^a-z0-9]/gi, '-')
      .toLowerCase();
    const filename = `${uuidv4()}-${sanitizedName}${ext}`;
    const key = `blog-images/${filename}`;

    await this.storage.upload({
      bucket: BLOG_IMAGES_BUCKET,
      key,
      body: file.buffer,
      contentType: file.mimetype,
    });

    this.logger.log(`[ADMIN] Blog image uploaded: ${key}`);
    return { url: `/api/v1/blog/images/${filename}`, filename };
  }

  @Get('blog')
  @ApiOperation({ summary: 'List all blog posts (Admin only)' })
  @ApiQuery({ name: 'status', required: false })
  @ApiQuery({ name: 'category', required: false })
  @ApiQuery({ name: 'search', required: false })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  @ApiQuery({ name: 'offset', required: false, type: Number })
  async listBlogPosts(
    @Query('status') status?: any,
    @Query('category') category?: BlogPostCategory,
    @Query('search') search?: string,
    @Query('limit') limit = 50,
    @Query('offset') offset = 0,
    @CurrentUser() user?: UserPayload
  ) {
    this.logger.log(`[ADMIN: ${user?.email}] Listing blog posts`);
    const { posts, total } = await this.blogService.listAllPosts({
      status,
      category,
      search,
      limit: Number(limit),
      offset: Number(offset),
    });
    return { posts: posts.map(this.mapBlogPostToDto), total };
  }

  @Post('blog')
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({ summary: 'Create a blog post (Admin only)' })
  async createBlogPost(@Body() dto: CreateBlogPostDto, @CurrentUser() user: UserPayload) {
    this.logger.log(`[ADMIN: ${user.email}] Creating blog post: ${dto.slug}`);
    const post = await this.blogService.createPost(dto);
    return this.mapBlogPostToDto(post);
  }

  @Patch('blog/:id')
  @UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
  @ApiOperation({ summary: 'Update a blog post (Admin only)' })
  async updateBlogPost(
    @Param('id', ParseUUIDPipe) id: string,
    @Body() dto: UpdateBlogPostDto,
    @CurrentUser() user: UserPayload
  ) {
    this.logger.log(`[ADMIN: ${user.email}] Updating blog post: ${id}`);
    const post = await this.blogService.updatePost(id, dto);
    return this.mapBlogPostToDto(post);
  }

  @Delete('blog/:id')
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: 'Delete a blog post (Admin only)' })
  async deleteBlogPost(
    @Param('id', ParseUUIDPipe) id: string,
    @CurrentUser() user: UserPayload
  ): Promise<void> {
    this.logger.log(`[ADMIN: ${user.email}] Deleting blog post: ${id}`);
    await this.blogService.deletePost(id);
  }

  private mapBlogPostToDto(post: BlogPost) {
    return {
      id: post.id,
      title: post.title,
      slug: post.slug,
      excerpt: post.excerpt,
      content: post.content,
      coverImageUrl: post.coverImageUrl,
      category: post.category,
      tags: post.tags,
      authorName: post.authorName,
      status: post.status,
      isFeatured: post.isFeatured,
      publishedAt: post.publishedAt,
      createdAt: post.createdAt,
      updatedAt: post.updatedAt,
    };
  }
}
