import {
    Body,
    Controller,
    Delete,
    Get,
    HttpException,
    HttpStatus,
    Param,
    Patch,
    Post,
    Put,
    Query,
    UploadedFile,
    UseGuards,
    UseInterceptors,
    ValidationPipe,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { FileInterceptor } from '@nestjs/platform-express';
import { ApiBearerAuth, ApiBody, ApiConsumes, ApiCreatedResponse, ApiExcludeEndpoint, ApiParam } from '@nestjs/swagger';
import { RequireModule } from 'src/auth/decorators/require-module.decorator';
import { RequirePermission } from 'src/auth/decorators/require-permission.decorator';
import type { LoginUserInfo } from 'src/auth/dto/login.dto';
import { GetUser } from 'src/auth/get-user.decorator';
import { ModuleGuard } from 'src/auth/guards/module.guard';
import { PermissionGuard } from 'src/auth/guards/permission.guard';

import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { envs } from 'src/config';
import { ChangeEmpresaPlanDto } from './dto/change-empresa-plan.dto';
import { CreateEmpresaDto } from './dto/create-empresa.dto';
import { CreateEmpresaFullDto } from './dto/create-empresa-full.dto';
import {
    body_empresa_certificado,
    response_empresa_all,
    response_empresa_find,
    response_empresa_plan,
    response_empresa_register,
} from './dto/doc-swagger';
import { TestEmailDto, UpsertEmpresaCorreoDto } from './dto/empresas-correo.dto';
import { LimiteOverrideDto } from './dto/limite-override.dto';
import { UpdateEmpresaDto } from './dto/update-empresa.dto';
import { UploadCertificadoDigitalDto } from './dto/upload-certificado-digital.dto';
import { body_empresa_logo, response_empresa_logo } from './dto/upload-logo.dto';
import { EmpresasService } from './empresas.service';
// @ApiExcludeController() // Oculta todo el controlador del Swagger
@Controller({ path: 'empresas', version: '1' })
@UseGuards(AuthGuard('jwt'), ModuleGuard, PermissionGuard)
@RequireModule('ADMINISTRACION')
export class EmpresasController {
  constructor(private readonly empresasService: EmpresasService) {}
  // @UseGuards(AuthGuard('jwt'))
  // @ApiBearerAuth()
  // @Post('timbrado')
  // @ApiBody({ type: CreateTimbradoDto })
  // @ApiCreatedResponse(response_timbrado)
  // async createTimbrado(@Body(ValidationPipe) createTimbradoDto: CreateTimbradoDto, @GetUser() user: LoginUserInfo) {
  //   const timbrado = await this.empresasService.createTimbrado(createTimbradoDto, user.empresa_id);
  //   return timbrado;
  // }

  @Post()
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_EDITAR')
  @ApiBearerAuth()
  async create(@Body(ValidationPipe) dto: CreateEmpresaFullDto, @GetUser() user: LoginUserInfo) {
    try {
      const result = await this.empresasService.create(dto, user.id);
      return {
        success: true,
        message: 'Empresa y usuario creados exitosamente',
        data: result,
      };
    } catch (error) {
      if (error instanceof HttpException) throw error;
      const message = error instanceof Error ? error.message : 'Error al crear empresa';
      throw new HttpException(
        {
          success: false,
          message,
          error: 'Bad Request',
        },
        HttpStatus.BAD_REQUEST,
      );
    }
  }

  @Post(':id/certificado')
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_EDITAR')
  @ApiBearerAuth()
  @ApiConsumes('multipart/form-data')
  @ApiParam({ name: 'id', type: 'string', required: true, description: 'ID del empresa' })
  @ApiBody(body_empresa_certificado)
  @ApiCreatedResponse(response_empresa_register)
  @UseInterceptors(FileInterceptor('file'))
  async uploadCertificadoDigital(
    @Param('id') id: string,
    @UploadedFile() file: Express.Multer.File,
    @Body(ValidationPipe) dto: UploadCertificadoDigitalDto,
    @GetUser() user: LoginUserInfo,
  ) {
    try {
      console.log(id, user.empresa_id);
      if (id !== user.empresa_id) {
        throw new HttpException(
          { success: false, message: 'No tienes permisos para esta empresa', error: 'Error' },
          HttpStatus.FORBIDDEN,
        );
      }

      if (!file?.buffer) {
        throw new HttpException(
          { success: false, message: 'Archivo inválido', error: 'Error' },
          HttpStatus.BAD_REQUEST,
        );
      }

      const maxBytes = 10 * 1024 * 1024;
      if (file.size > maxBytes) {
        throw new HttpException(
          { success: false, message: 'El archivo excede el tamaño máximo permitido (5MB)', error: 'Error' },
          HttpStatus.BAD_REQUEST,
        );
      }

      const name = (file.originalname || '').toLowerCase();
      if (!name.endsWith('.p12') && !name.endsWith('.pfx')) {
        throw new HttpException(
          { success: false, message: 'Formato inválido. Solo .p12 o .pfx', error: 'Error' },
          HttpStatus.BAD_REQUEST,
        );
      }

      const result = await this.empresasService.uploadCertificadoDigital({
        empresaId: id,
        usuario: user.id,
        pin: dto.pin,
        fileBuffer: file.buffer,
        originalFilename: file.originalname,
      });

      return {
        success: true,
        message: 'Certificado validado exitósamente',
        data: result,
      };
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Error al subir certificado';
      const status = message.includes('permisos') ? HttpStatus.FORBIDDEN : HttpStatus.BAD_REQUEST;
      throw new HttpException(
        {
          success: false,
          message,
          error: 'Error',
        },
        status,
      );
    }
  }

  @Patch(':id/plan')
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_EDITAR')
  @ApiBearerAuth()
  @ApiParam({ name: 'id', type: 'string', required: true, description: 'ID del empresa' })
  @ApiBody({ type: ChangeEmpresaPlanDto })
  @ApiCreatedResponse(response_empresa_plan)
  async changePlan(
    @Param('id') id: string,
    @Body(ValidationPipe) dto: ChangeEmpresaPlanDto,
    @GetUser() user: LoginUserInfo,
  ) {
    try {
      const fechaInicio = dto.fecha_inicio ? new Date(dto.fecha_inicio) : undefined;
      const fechaFin = dto.fecha_fin ? new Date(dto.fecha_fin) : undefined;
      const suscripcion = await this.empresasService.changePlan(
        id,
        dto.plan_id,
        fechaInicio,
        dto.motivo,
        user?.id,
        fechaFin,
        dto.costo_mensual,
        dto.dia_cobro,
      );
      return {
        success: true,
        message: 'Plan actualizado exitosamente',
        data: suscripcion,
      };
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Error al cambiar plan';
      const status = message.includes('no encontrada') ? HttpStatus.NOT_FOUND : HttpStatus.BAD_REQUEST;
      throw new HttpException(
        {
          success: false,
          message,
          error: 'Error',
        },
        status,
      );
    }
  }

  @Patch(':id/limite-override')
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_EDITAR')
  @ApiBearerAuth()
  @ApiParam({ name: 'id', type: 'string', required: true, description: 'ID de la empresa' })
  @ApiBody({ type: LimiteOverrideDto })
  async applyLimiteOverride(
    @Param('id') id: string,
    @Body(ValidationPipe) dto: LimiteOverrideDto,
    @GetUser() user: LoginUserInfo,
  ) {
    try {
      const { expira_at, nota, ...limites } = dto;
      const expiraDate = expira_at ? new Date(expira_at) : null;
      const result = await this.empresasService.applyOverride(id, limites, expiraDate, nota ?? null, user?.id);
      return { success: true, message: 'Override de límites aplicado', data: result };
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Error al aplicar override';
      const status = message.includes('no encontrad') ? HttpStatus.NOT_FOUND : HttpStatus.BAD_REQUEST;
      throw new HttpException({ success: false, message, error: 'Error' }, status);
    }
  }

  @Delete(':id/limite-override')
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_EDITAR')
  @ApiBearerAuth()
  @ApiParam({ name: 'id', type: 'string', required: true, description: 'ID de la empresa' })
  async clearLimiteOverride(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    try {
      const result = await this.empresasService.clearOverride(id, user?.id);
      return { success: true, ...result };
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Error al limpiar override';
      const status = message.includes('no encontrad') ? HttpStatus.NOT_FOUND : HttpStatus.BAD_REQUEST;
      throw new HttpException({ success: false, message, error: 'Error' }, status);
    }
  }
  @ApiBearerAuth()
  @ApiParam({ name: 'page', type: 'string', required: false, description: 'Pagina actual' })
  @ApiParam({ name: 'limit', type: 'string', required: false, description: 'Limit' })
  @ApiCreatedResponse(response_empresa_all)
  @Get()
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_VER')
  async findAll(@Query('page') page?: string, @Query('limit') limit?: string) {
    try {
      const {
        items,
        total,
        page: currentPage,
        limit: pageLimit,
        lastPage,
      } = await this.empresasService.findAll(Number(page) || 1, Number(limit) || 10);

      return {
        message: 'Empresas obtenidas exitosamente',
        data: items,
        total,
        page: currentPage,
        limit: pageLimit,
        lastPage,
      };
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
      throw new HttpException(
        {
          success: false,
          message: 'Error al obtener empresas',
          error: errorMessage,
        },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  @ApiExcludeEndpoint()
  @Get('estados')
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_VER')
  @ApiBearerAuth()
  async getEstados() {
    const estados = await this.empresasService.getEstadosEmpresa();
    return { success: true, data: estados };
  }

  @ApiBearerAuth()
  @ApiParam({ name: 'id', type: 'string', required: true, description: 'ID del empresa' })
  @ApiCreatedResponse(response_empresa_find)
  @Get(':id')
  async findOne(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    // Acceso libre a la propia empresa (boot del frontend).
    // Para leer otra empresa se exige ADM_EMP_EMPRESA_VER vía guard manual.
    if (id !== user.empresa_id) {
      await this.empresasService.requirePermisoLecturaOtraEmpresa(user, id);
    }
    return this.empresasService.findOne(id);
  }
  @Post(':id/logo')
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_EDITAR')
  @ApiBearerAuth()
  @ApiConsumes('multipart/form-data')
  @ApiParam({ name: 'id', type: 'string', required: true, description: 'ID de la empresa' })
  @ApiBody(body_empresa_logo)
  @ApiCreatedResponse(response_empresa_logo)
  @UseInterceptors(FileInterceptor('file'))
  async uploadLogo(@Param('id') id: string, @UploadedFile() file: Express.Multer.File, @GetUser() user: LoginUserInfo) {
    try {
      if (id !== user.empresa_id) {
        throw new HttpException(
          { success: false, message: 'No tienes permisos para esta empresa', error: 'Error' },
          HttpStatus.FORBIDDEN,
        );
      }

      if (!file?.buffer) {
        throw new HttpException(
          { success: false, message: 'Archivo inválido', error: 'Error' },
          HttpStatus.BAD_REQUEST,
        );
      }

      const maxBytes = 5 * 1024 * 1024; // 5MB
      if (file.size > maxBytes) {
        throw new HttpException(
          { success: false, message: 'El archivo excede el tamaño máximo permitido (5MB)', error: 'Error' },
          HttpStatus.BAD_REQUEST,
        );
      }

      const allowedMimeTypes = new Set(['image/png', 'image/jpeg', 'image/jpg', 'image/webp']);
      if (!file.mimetype || !allowedMimeTypes.has(file.mimetype)) {
        throw new HttpException(
          { success: false, message: 'Tipo de archivo no permitido. Solo PNG, JPG, JPEG o WEBP.', error: 'Error' },
          HttpStatus.BAD_REQUEST,
        );
      }

      // Subir a DigitalOcean Spaces
      const s3Client = new S3Client({
        endpoint: envs.doSpacesEndpoint,
        region: envs.doSpacesRegion,
        credentials: {
          accessKeyId: envs.doSpacesKey,
          secretAccessKey: envs.doSpacesSecret,
        },
      });

      const ext = file.originalname.split('.').pop() || 'png';
      const fileName = `logos/${id}.${ext}`;

      await s3Client.send(
        new PutObjectCommand({
          Bucket: envs.doSpacesBucket,
          Key: fileName,
          Body: file.buffer,
          ContentType: file.mimetype,
          ACL: 'public-read',
        }),
      );

      const logoUrl = `${envs.doSpacesPublicUrl}/${fileName}`;
      const result = await this.empresasService.uploadLogo(id, logoUrl, user.id);

      return {
        success: true,
        message: 'Logo actualizado exitosamente',
        data: result,
      };
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Error al subir logo';
      const status = message.includes('permisos') ? HttpStatus.FORBIDDEN : HttpStatus.BAD_REQUEST;
      throw new HttpException(
        {
          success: false,
          message,
          error: 'Error',
        },
        status,
      );
    }
  }

  @Put(':id')
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_EDITAR')
  @ApiBearerAuth()
  @ApiParam({ name: 'id', type: 'string', required: true, description: 'ID del empresa' })
  @ApiBody({ type: UpdateEmpresaDto })
  @ApiCreatedResponse(response_empresa_find)
  async update(
    @Param('id') id: string,
    @Body(ValidationPipe) updateEmpresaDto: UpdateEmpresaDto,
    @GetUser() user: LoginUserInfo,
  ) {
    try {
      const empresa = await this.empresasService.update(id, updateEmpresaDto, user?.id);
      return {
        success: true,
        message: 'Empresa actualizada exitosamente',
        data: empresa,
      };
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Error al actualizar empresa';
      const status = message.includes('no encontrada') ? HttpStatus.NOT_FOUND : HttpStatus.BAD_REQUEST;

      throw new HttpException(
        {
          success: false,
          message,
          error: 'Error',
        },
        status,
      );
    }
  }
  @Patch(':id/estado')
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_EDITAR')
  @ApiBearerAuth()
  async cambiarEstado(
    @Param('id') id: string,
    @Body('estado_id') estadoId: string,
    @GetUser() user: LoginUserInfo,
  ) {
    const result = await this.empresasService.cambiarEstadoEmpresa(id, estadoId, user.id);
    return { success: true, message: 'Estado actualizado', data: result };
  }

  @Delete(':id')
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_EDITAR')
  async remove(@Param('id') id: string, @GetUser() user?: LoginUserInfo) {
    try {
      const result = await this.empresasService.remove(id, user?.id);
      return {
        success: true,
        message: result.message,
        data: result.empresa,
      };
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Error al buscar empresa';
      const status = message.includes('no encontrada') ? HttpStatus.NOT_FOUND : HttpStatus.INTERNAL_SERVER_ERROR;

      throw new HttpException(
        {
          success: false,
          message,
          error: 'Error',
        },
        status,
      );
    }
  }

  @Delete(':id/force')
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_EDITAR')
  @ApiBearerAuth()
  @ApiParam({ name: 'id', type: 'string', required: true, description: 'ID de la empresa a eliminar permanentemente' })
  async forceDelete(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    try {
      const result = await this.empresasService.forceDeleteEmpresa(id, user.empresa_id, user?.id);
      return { success: true, message: result.message };
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Error al eliminar empresa';
      const status =
        message.includes('no encontrada') ? HttpStatus.NOT_FOUND :
        message.includes('Solo') || message.includes('pertenecen') || message.includes('propia') ? HttpStatus.FORBIDDEN :
        HttpStatus.BAD_REQUEST;
      throw new HttpException({ success: false, message, error: 'Error' }, status);
    }
  }

  // ─── Correo de Notificación ───────────────────────────────────────

  @Get(':id/correo')
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_VER')
  @ApiBearerAuth()
  async getCorreo(@Param('id') id: string) {
    try {
      const correo = await this.empresasService.getEmpresaCorreo(id);
      return { success: true, data: correo };
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Error al obtener correo';
      throw new HttpException({ success: false, message }, HttpStatus.BAD_REQUEST);
    }
  }

  @Put(':id/correo')
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_EDITAR')
  @ApiBearerAuth()
  async upsertCorreo(
    @Param('id') id: string,
    @Body(ValidationPipe) dto: UpsertEmpresaCorreoDto,
    @GetUser() user: LoginUserInfo,
  ) {
    try {
      const result = await this.empresasService.upsertEmpresaCorreo(id, dto, user?.id);
      return { success: true, message: 'Correo configurado exitosamente', data: result };
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Error al configurar correo';
      throw new HttpException({ success: false, message }, HttpStatus.BAD_REQUEST);
    }
  }

  @Delete(':id/correo')
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_EDITAR')
  @ApiBearerAuth()
  async deleteCorreo(@Param('id') id: string, @GetUser() user: LoginUserInfo) {
    try {
      const result = await this.empresasService.deleteEmpresaCorreo(id, user?.id);
      return { success: true, ...result };
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Error al eliminar correo';
      const status = message.includes('no encontrad') ? HttpStatus.NOT_FOUND : HttpStatus.BAD_REQUEST;
      throw new HttpException({ success: false, message }, status);
    }
  }

  @Post(':id/correo/test')
  @RequirePermission('ADMINISTRACION', 'ADM_EMP_EMPRESA_EDITAR')
  @ApiBearerAuth()
  async testCorreo(@Param('id') id: string, @Body(ValidationPipe) dto: TestEmailDto) {
    try {
      const result = await this.empresasService.testEmpresaCorreo(id, dto.destinatario);
      return { success: true, ...result };
    } catch (error) {
      const message = error instanceof Error ? error.message : 'Error al enviar correo de prueba';
      throw new HttpException({ success: false, message }, HttpStatus.BAD_REQUEST);
    }
  }
}
