import { prisma, tenantDb } from '../config/database.js';
import { hashPassword } from '../auth/passwords.js';
import { generateAccessToken, generateRefreshToken } from '../auth/tokens.js';
import type { DespachoSignupRequest } from '@horux/shared';
import type { JWTPayload, Role } from '@horux/shared';
import { emailService } from './email/email.service.js';

export async function signupDespacho(data: DespachoSignupRequest) {
  const { despacho, owner } = data;

  const existingUser = await prisma.user.findUnique({ where: { email: owner.email } });
  if (existingUser) {
    throw new Error('Ya existe un usuario con este email');
  }

  const passwordHash = await hashPassword(owner.password);

  const tenantSlug = `despacho_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;
  const databaseName = `horux_${tenantSlug}`;

  const result = await prisma.$transaction(async (tx) => {
    const tenant = await tx.tenant.create({
      data: {
        nombre: despacho.nombre,
        rfc: tenantSlug.toUpperCase(),
        plan: 'enterprise',
        databaseName: databaseName,
        cfdiLimit: -1,
        usersLimit: -1,
        verticalProfile: despacho.verticalProfile as any,
        dbMode: (despacho.plan === 'business_control' ? 'BYO' : 'MANAGED') as any,
        dbSchemaVersion: 0,
        trialEndsAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
        codigoPostal: despacho.codigoPostal,
      },
    });

    const user = await tx.user.create({
      data: {
        email: owner.email.toLowerCase(),
        passwordHash,
        nombre: owner.nombre,
        lastTenantId: tenant.id,
      },
    });

    const ownerRole = await tx.rol.findUnique({ where: { nombre: 'owner' } });
    if (!ownerRole) throw new Error('Rol owner no encontrado en BD');

    await tx.tenantMembership.create({
      data: {
        userId: user.id,
        tenantId: tenant.id,
        rolId: ownerRole.id,
        isOwner: true,
      },
    });

    return { tenant, user };
  });

  try {
    await tenantDb.provisionDatabase(tenantSlug, databaseName);
  } catch (err: any) {
    await prisma.tenant.delete({ where: { id: result.tenant.id } }).catch(() => {});
    await prisma.user.delete({ where: { id: result.user.id } }).catch(() => {});
    throw new Error(`Error al crear base de datos del despacho: ${err.message}`);
  }

  const payload: Omit<JWTPayload, 'iat' | 'exp'> = {
    userId: result.user.id,
    email: result.user.email,
    role: 'owner' as Role,
    tenantId: result.tenant.id,
    tokenVersion: 0,
  };

  const accessToken = generateAccessToken(payload);
  const refreshToken = generateRefreshToken(payload);

  await prisma.refreshToken.create({
    data: {
      userId: result.user.id,
      token: refreshToken,
      expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
    },
  });

  // Send welcome email (fire-and-forget)
  emailService.sendDespachoWelcome(owner.email, {
    nombre: result.user.nombre,
    despachoNombre: result.tenant.nombre,
    email: result.user.email,
  }).catch(err => console.error('[Signup] Welcome email failed:', err));

  // If paid plan, create MP checkout via subscriptionService.subscribe()
  // que también crea la fila Subscription en BD (clave para que el webhook
  // pueda aplicar la dualidad firstYear→renewal tras el primer cobro aprobado).
  let paymentUrl: string | undefined;
  if (data.despacho.plan && data.despacho.plan !== 'trial') {
    try {
      const subscriptionService = await import('./payment/subscription.service.js');
      const result2 = await subscriptionService.subscribe({
        tenantId: result.tenant.id,
        plan: data.despacho.plan as any,
        frequency: 'annual',
        payerEmail: owner.email,
      });
      paymentUrl = result2.paymentUrl;
    } catch (err: any) {
      // Rollback: delete tenant + user since payment couldn't be set up
      await prisma.tenantMembership.deleteMany({ where: { tenantId: result.tenant.id } }).catch(() => {});
      await prisma.refreshToken.deleteMany({ where: { userId: result.user.id } }).catch(() => {});
      await prisma.tenant.delete({ where: { id: result.tenant.id } }).catch(() => {});
      await prisma.user.delete({ where: { id: result.user.id } }).catch(() => {});
      const msg = err?.message || '';
      if (msg.includes('MercadoPago no está configurado') || msg.includes('Unauthorized access')) {
        throw new Error('No se pudo procesar el cobro. Verifica que el sistema de pagos esté configurado o selecciona el plan Trial.');
      }
      throw new Error(msg || 'No se pudo procesar el cobro.');
    }
  }

  return {
    accessToken,
    refreshToken,
    paymentUrl,
    user: {
      id: result.user.id,
      email: result.user.email,
      nombre: result.user.nombre,
      role: 'owner' as Role,
      tenantId: result.tenant.id,
      tenantName: result.tenant.nombre,
      tenantRfc: result.tenant.rfc,
      plan: result.tenant.plan,
      tenants: [{
        id: result.tenant.id,
        nombre: result.tenant.nombre,
        rfc: result.tenant.rfc,
        plan: result.tenant.plan,
        role: 'owner' as Role,
        isOwner: true,
      }],
    },
  };
}
