import { ConflictException, Injectable, NotFoundException } from '@nestjs/common';
import * as bcrypt from 'bcrypt';
import { envs } from 'src/config';
import { PrismaService } from 'src/prisma/prisma.service';
import { EstadoSuscripcion, tipoModuloSuscripcion } from 'src/suscripciones/dto/enum-sucripcion';
import { EstadoUser } from 'src/users/dto/enum-user';
import { calcularPrimerCobro } from '../utils/ciclo-suscripcion';

export interface RegistroEmpresaInput {
  ruc: string;
  dv: string;
  razon_social: string;
  nombre_fantasia?: string;
  tipo_contribuyente: number;
  celular: string;
  email: string;
  usuario: string;
  password: string;
  nombres_usuario?: string;
  plan_id?: string | null;
  parent_id?: string | null;
  type?: string;
  precio_acordado?: number | null;
  fecha_inicio_plan?: string | null;
  dia_cobro?: number | null;
  notas_comerciales?: string | null;
}

export interface RegistroEmpresaResult {
  empresa: { id: string; razon_social: string; ruc: string; type: string | null };
  usuario: { id: string; username: string | null };
  suscripcion: { id: string };
}

@Injectable()
export class RegistroEmpresaService {
  constructor(private readonly prisma: PrismaService) {}

  private addDays(date: Date, d: number) {
    const copy = new Date(date);
    copy.setDate(copy.getDate() + d);
    return copy;
  }

  async registrar(input: RegistroEmpresaInput): Promise<RegistroEmpresaResult> {
    // ── Validaciones previas ──
    const existingEmpresa = await this.prisma.empresas.findUnique({
      where: { ruc: input.ruc },
    });
    if (existingEmpresa) throw new ConflictException('Ya existe una empresa con este RUC');

    const existingUser = await this.prisma.usuario.findUnique({
      where: { username: input.usuario },
    });
    if (existingUser) throw new ConflictException('El nombre de usuario ya existe');

    const estadoInicial = await this.prisma.estado_empresa.findFirst({ where: { codigo: '1' } });
    if (!estadoInicial) throw new NotFoundException('Estado inicial no encontrado');

    const tipo_contribuyente = await this.prisma.tipo_contribuyente.findFirst({
      where: { codigo: input.tipo_contribuyente },
    });
    if (!tipo_contribuyente) throw new NotFoundException('Tipo contribuyente no encontrado');

    const plan = input.plan_id
      ? await this.prisma.planes.findUnique({ where: { id: input.plan_id }, include: { plan_modulos: true } })
      : await this.prisma.planes.findFirst({ where: { codigo: '1' }, include: { plan_modulos: true } });
    if (!plan) throw new NotFoundException('Plan no encontrado');
    if (plan.plan_modulos.length === 0) throw new NotFoundException('Plan no tiene módulos');

    const perfilAdmin = await this.prisma.perfiles.findFirst({ where: { perfil: 'superAdmin' } });
    if (!perfilAdmin) throw new NotFoundException('Perfil superAdmin no encontrado');

    const moneda = await this.prisma.moneda.findFirst({ where: { codigo: 'PYG' } });
    if (!moneda) throw new NotFoundException('Moneda PYG no encontrada');

    const medio_pagos = await this.prisma.medio_pago.findMany();
    if (!medio_pagos?.length) throw new NotFoundException('Medio de pago no encontrado');

    // ── Determinar parent_id y type ──
    let parentId = input.parent_id || null;
    let empresaType = input.type || 'subsidiary';

    if (!parentId && envs.holdingEmpresaId) {
      const holdingEmpresa = await this.prisma.empresas.findUnique({
        where: { id: envs.holdingEmpresaId },
      });
      if (holdingEmpresa) {
        parentId = envs.holdingEmpresaId;
        empresaType = 'subsidiary';
      }
    }

    if (parentId) {
      const parentEmpresa = await this.prisma.empresas.findUnique({ where: { id: parentId } });
      if (!parentEmpresa) throw new NotFoundException('Empresa padre no encontrada');
    }

    // ── Hash passwords ──
    const saltRounds = 10;
    const hashedPassword = await bcrypt.hash(input.password, saltRounds);
    const hashedPasswordFuncional = await bcrypt.hash(`ABC#${input.ruc}`, saltRounds);

    // ── Transacción atómica ──
    return this.prisma.$transaction(async (tx) => {
      // 1. Empresa
      const empresa = await tx.empresas.create({
        data: {
          ruc: input.ruc,
          dv: input.dv,
          razon_social: input.razon_social,
          nombre_fantasia: input.nombre_fantasia || null,
          tipo_contribuyente: tipo_contribuyente.id,
          celular: input.celular,
          email: input.email,
          active: true,
          produccion: false,
          celular_validado: true,
          email_validado: false,
          estado_id: estadoInicial.id,
          parent_id: parentId,
          type: empresaType,
        },
      });

      // 2. Suscripción
      const fechaInicio = input.fecha_inicio_plan ? new Date(input.fecha_inicio_plan) : new Date();
      const precioAcordado = input.precio_acordado ?? Number(plan.precio ?? 0);

      const fechaProxCobro = calcularPrimerCobro(fechaInicio, input.dia_cobro);
      const fechaFin = fechaProxCobro; // el período termina cuando vence el próximo cobro

      const suscripcion = await tx.suscripciones.create({
        data: {
          empresa_id: empresa.id,
          plan_id: plan.id,
          fecha_inicio: fechaInicio,
          fecha_fin: fechaFin,
          estado: EstadoSuscripcion.Activo,
          fecha_prox_cobro: fechaProxCobro,
          descripcion: 'Suscripcion plan Inicial',
          costo_mensual: precioAcordado,
          precio_venta: precioAcordado,
          precio_costo: Number(plan.precio ?? 0),
          dia_cobro: input.dia_cobro ?? null,
          notas_comerciales: input.notas_comerciales ?? null,
        },
      });

      // 3. Módulos del plan
      await tx.suscripcion_modulos.createMany({
        data: plan.plan_modulos.map((item) => ({
          suscripcion_id: suscripcion.id,
          modulo_id: item.modulo_id,
          tipo: tipoModuloSuscripcion.Incluido,
          costo_adicional: 0,
        })),
      });

      // 4. Usuario admin
      const usuario = await tx.usuario.create({
        data: {
          nombres: input.nombres_usuario || 'Admin',
          username: input.usuario,
          password: hashedPassword,
          estado: EstadoUser.Activo,
          is_superadmin: true,
          empresa_id: empresa.id,
        },
      });

      // 5. Perfil superAdmin
      await tx.usuario_perfiles.create({
        data: { user_id: usuario.id, perfil_id: perfilAdmin.id },
      });

      // 6. Usuario funcional (middleware)
      await tx.usuario_funcional.create({
        data: {
          username: input.ruc,
          password: hashedPasswordFuncional,
          active: true,
          verificado: true,
          estado: 'activo',
          empresa: empresa.id,
        },
      });

      // 7. Condición de pago "Contado"
      await tx.condiciones_pago.create({
        data: {
          empresa_id: empresa.id,
          descripcion: 'Contado',
          dias_plazo: 0,
          cuotas: 1,
          cuota_inicial: false,
          cod_condicion_venta: 1,
          cod_tipo_credito: 1,
          es_default: true,
          es_sistema: true,
          activo: true,
        },
      });

      // 8. Cliente "Sin nombre"
      const personaSinNombre = await tx.personas.create({
        data: {
          empresa_id: empresa.id,
          tipo_documento_id: '3fd2cd12-3e35-460f-a5b1-72f6e20817e4',
          ciudad_id: null,
          naturaleza_id: '50093f82-e37d-4120-ad06-cdfdfffa7ec7',
          tipo_contribuyente_id: null,
          razon_social: 'Sin nombre',
          direccion: null,
          nro_casa: 0,
          celular: null,
          email: null,
          ruc: null,
          dv: null,
          nro_documento: '0',
          email_adicional: null,
          email_copia: null,
        },
      });

      await tx.clientes.create({
        data: {
          persona_id: personaSinNombre.id,
          tipo_operacion_id: 'f4464d3e-b711-424c-9833-84a9d4eca8e8',
          cod_cliente: 'SN001',
        },
      });

      // 9. Producto "Genérico"
      await tx.productos.create({
        data: {
          empresa_id: empresa.id,
          cod_producto: 'GEN0001',
          descripcion: 'Generico',
          precio: 0,
          porcentaje_iva: 10,
          proporcion_iva: 100,
          tipo: 'servicio',
          afectacion_id: '7b634413-a746-4152-9941-4d586ed75825',
          unidad_medida_id: '9d8da927-aa3f-40f1-8fd0-19d513133671',
        },
      });

      // 10. Moneda PYG
      await tx.empresa_monedas.create({
        data: {
          empresa_id: empresa.id,
          moneda_id: moneda.id,
          es_principal: true,
          decimales_visualizar: 0,
          active: true,
        },
      });

      // 11. Medios de pago
      for (const medioPago of medio_pagos) {
        await tx.empresa_medio_pago.create({
          data: {
            empresa_id: empresa.id,
            medio_pago_id: medioPago.id,
            activo: true,
          },
        });
      }

      return { empresa, usuario, suscripcion };
    });
  }
}
