﻿import {
  Body,
  Controller,
  ForbiddenException,
  Get,
  HttpCode,
  HttpStatus,
  Param,
  ParseBoolPipe,
  ParseIntPipe,
  Patch,
  Post,
  Put,
  Query,
  Request,
  UseGuards,
} from "@nestjs/common";
import {
  ApiBearerAuth,
  ApiOperation,
  ApiResponse,
  ApiTags,
} from "@nestjs/swagger";
import { AuthService } from "./auth.service";
import { CreateUserDto } from "./dto/create-user.dto";
import { LoginDto } from "./dto/login.dto";
import { RefreshTokenDto } from "./dto/refresh-token.dto";
import { SwitchTenantDto } from "./dto/switch-tenant.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
import { UpdateTiendaPrintDto } from "./dto/update-tienda-print.dto";
import { CreateTiendaDto } from "./dto/create-tienda.dto";
import { UpdateTiendaFacturacionPosDto } from "./dto/update-tienda-facturacion-pos.dto";
import { UserRole } from "./entities/usuario.entity";
import { JwtAuthGuard } from "./guards/jwt-auth.guard";
import { PermissionGuard } from "./guards/permission.guard";
import { RequirePermission } from "./decorators/require-permission.decorator";
import { nivelConfiguracion } from "./default-permissions";

@ApiTags("auth")
@Controller("auth")
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  // Chequeo inline (en vez de @RequirePermission) porque además de
  // "configuracion.facturacion" en escritura valida la restricción opcional
  // por tienda (facturacionPosTiendaIds) -- ver también nivelConfiguracion.
  private ensureFacturacionPosAccess(req: any, tiendaId: number) {
    if (nivelConfiguracion(req?.user?.permissions, "facturacion") !== "write") {
      throw new ForbiddenException(
        "No tenés permiso de escritura en facturación (configuración).",
      );
    }
    const restriccion = req?.user?.facturacionPosTiendaIds;
    if (Array.isArray(restriccion) && !restriccion.includes(tiendaId)) {
      throw new ForbiddenException(
        "No tenés acceso de facturación POS para esta tienda.",
      );
    }
  }

  // super_admin solo lo asigna otro super_admin -- sin esto, un admin podria
  // mandar rol="super_admin" directo por API aunque el selector del
  // frontend no se lo ofrezca (ese solo oculta la opcion, no la bloquea).
  private ensureCanAssignRole(req: any, rol: UserRole | undefined) {
    if (rol === UserRole.SUPER_ADMIN && req?.user?.rol !== UserRole.SUPER_ADMIN) {
      throw new ForbiddenException(
        "Solo un super admin puede asignar el rol super_admin",
      );
    }
  }

  @Post("login")
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: "Iniciar sesión" })
  @ApiResponse({ status: 200, description: "Login exitoso" })
  @ApiResponse({ status: 401, description: "Credenciales inválidas" })
  async login(@Body() loginDto: LoginDto) {
    return this.authService.login(loginDto);
  }

  @Post("refresh")
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: "Renovar token de acceso" })
  @ApiResponse({ status: 200, description: "Token renovado" })
  @ApiResponse({ status: 401, description: "Token inválido" })
  async refresh(@Body() refreshTokenDto: RefreshTokenDto) {
    return this.authService.refreshToken(refreshTokenDto.refreshToken);
  }

  @Get("me")
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @ApiOperation({ summary: "Obtener perfil del usuario actual" })
  @ApiResponse({ status: 200, description: "Perfil del usuario" })
  async getProfile(@Request() req: any) {
    return this.authService.getProfile(req.user.id, req.user.tenantId);
  }

  @Get("tenants-disponibles")
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @ApiOperation({ summary: "Listar los tenants a los que esta identidad puede cambiarse" })
  async listTenantsDisponibles(@Request() req: any) {
    return this.authService.listAvailableTenants(
      req.user.identidadId,
      req.user.rolPlataforma,
      req.user.tenantId,
    );
  }

  @Post("switch-tenant")
  @HttpCode(HttpStatus.OK)
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @ApiOperation({ summary: "Cambiar de tenant sin volver a loguearse — reemite el token" })
  async switchTenant(@Body() dto: SwitchTenantDto, @Request() req: any) {
    return this.authService.switchTenant(req.user.identidadId, dto.tenantCodigo);
  }

  @Get("users")
  @UseGuards(JwtAuthGuard, PermissionGuard)
  @RequirePermission("usuarios", "read")
  @ApiBearerAuth()
  @ApiOperation({ summary: "Listar usuarios del sistema" })
  async listUsers(@Request() req: any) {
    return this.authService.listUsers(req.user.tenantId);
  }

  // "tiendas" no es un módulo de permiso aparte: gestionar tiendas (crearlas,
  // editar su encabezado de impresión) es la misma responsabilidad que
  // gestionar usuarios, y comparte su permiso granular "usuarios" -- ver
  // dashboard-shell.tsx (nav de "Tiendas" también gateado por "usuarios").
  @Get("tiendas")
  @UseGuards(JwtAuthGuard, PermissionGuard)
  @RequirePermission("usuarios", "read")
  @ApiBearerAuth()
  @ApiOperation({ summary: "Listar tiendas activas" })
  async listTiendas(@Request() req: any) {
    return this.authService.listTiendas(req.user.tenantId);
  }

  @Post("tiendas")
  @UseGuards(JwtAuthGuard, PermissionGuard)
  @RequirePermission("usuarios", "write")
  @ApiBearerAuth()
  @ApiOperation({
    summary:
      "Crear una tienda nueva (solo para tenants sin ERP/POSTouch real detras -- una tienda que ya viene de un ERP se sincroniza, no se crea aca)",
  })
  async createTienda(@Body() dto: CreateTiendaDto, @Request() req: any) {
    return this.authService.createTienda(req.user.tenantId, dto);
  }

  @Put("tiendas/:id")
  @UseGuards(JwtAuthGuard, PermissionGuard)
  @RequirePermission("usuarios", "write")
  @ApiBearerAuth()
  @ApiOperation({ summary: "Actualizar datos de impresion de una tienda" })
  async updateTienda(
    @Param("id", ParseIntPipe) id: number,
    @Body() dto: UpdateTiendaPrintDto,
    @Request() req: any,
  ) {
    return this.authService.updateTiendaPrintConfig(req.user.tenantId, id, dto);
  }

  @Put("tiendas/:id/facturacion-pos")
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @ApiOperation({ summary: "Configurar (o desactivar) el envio automatico de OTs a POSTouch para esta tienda" })
  async updateTiendaFacturacionPos(
    @Param("id", ParseIntPipe) id: number,
    @Body() dto: UpdateTiendaFacturacionPosDto,
    @Request() req: any,
  ) {
    this.ensureFacturacionPosAccess(req, id);
    return this.authService.updateTiendaFacturacionPos(req.user.tenantId, id, dto, req.user.usuarioLocalId);
  }

  @Post("users")
  @UseGuards(JwtAuthGuard, PermissionGuard)
  @RequirePermission("usuarios", "write")
  @ApiBearerAuth()
  @ApiOperation({ summary: "Crear usuario" })
  async createUser(@Body() dto: CreateUserDto, @Request() req: any) {
    this.ensureCanAssignRole(req, dto.rol);
    return this.authService.createUser(req.user.tenantId, dto, req.user.identidadId);
  }

  @Put("users/:id")
  @UseGuards(JwtAuthGuard, PermissionGuard)
  @RequirePermission("usuarios", "write")
  @ApiBearerAuth()
  @ApiOperation({ summary: "Actualizar usuario" })
  async updateUser(
    @Param("id", ParseIntPipe) id: number,
    @Body() dto: UpdateUserDto,
    @Request() req: any,
  ) {
    this.ensureCanAssignRole(req, dto.rol);
    return this.authService.updateUser(req.user.tenantId, id, dto, req.user.identidadId);
  }

  @Patch("users/:id/activo")
  @UseGuards(JwtAuthGuard, PermissionGuard)
  @RequirePermission("usuarios", "write")
  @ApiBearerAuth()
  @ApiOperation({ summary: "Activar/Desactivar usuario" })
  async setActive(
    @Param("id", ParseIntPipe) id: number,
    @Query("value", ParseBoolPipe) value: boolean,
    @Request() req: any,
  ) {
    return this.authService.setUserActive(req.user.tenantId, id, value);
  }

  @Post("logout")
  @UseGuards(JwtAuthGuard)
  @ApiBearerAuth()
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: "Cerrar sesión" })
  async logout() {
    return { message: "Sesión cerrada exitosamente" };
  }
}
