import { prisma, tenantDb } from '../config/database.js';
import { PLANS, DESPACHO_PLANS } from '@horux/shared';
import { emailService } from './email/email.service.js';
import * as metabaseService from './metabase.service.js';
import { randomBytes } from 'crypto';
import bcrypt from 'bcryptjs';

export async function getAllTenants() {
  return prisma.tenant.findMany({
    where: { active: true },
    select: {
      id: true,
      nombre: true,
      rfc: true,
      plan: true,
      databaseName: true,
      createdAt: true,
      _count: {
        select: { memberships: { where: { active: true } } as any }
      }
    },
    orderBy: { nombre: 'asc' }
  });
}

export async function getTenantById(id: string) {
  return prisma.tenant.findUnique({
    where: { id },
    select: {
      id: true,
      nombre: true,
      rfc: true,
      plan: true,
      databaseName: true,
      cfdiLimit: true,
      usersLimit: true,
      createdAt: true,
    }
  });
}

export async function createTenant(data: {
  nombre: string;
  rfc: string;
  plan?: string;
  verticalProfile?: 'CONTABLE' | 'JURIDICO' | 'ARQUITECTURA';
  frequency?: 'monthly' | 'annual';
  adminEmail: string;
  adminNombre: string;
  amount: number;
}) {
  const plan = data.plan || 'trial';
  const despachoPlans = ['trial', 'mi_empresa', 'mi_empresa_plus', 'business_control', 'business_cloud', 'custom'];
  const isDespacho = despachoPlans.includes(plan);

  let rfc = data.rfc.toUpperCase();
  let cfdiLimit: number;
  let usersLimit: number;
  let dbMode: 'BYO' | 'MANAGED' | undefined;
  let trialEndsAt: Date | undefined;
  let timbresIncluidos = 0;

  if (isDespacho) {
    // Normalizar RFC con prefijo DESPACHO_
    if (!rfc.startsWith('DESPACHO_')) {
      rfc = `DESPACHO_${rfc}`;
    }
    const despachoConfig = (DESPACHO_PLANS as Record<string, { maxRfcs: number; maxUsers: number; dbMode: 'BYO' | 'MANAGED'; timbresIncluidosMes: number }>)[plan];
    if (!despachoConfig) throw new Error(`Plan despacho desconocido: ${plan}`);
    cfdiLimit = -1;
    usersLimit = -1;
    dbMode = despachoConfig.dbMode;
    timbresIncluidos = despachoConfig.timbresIncluidosMes;
    if (plan === 'trial') {
      trialEndsAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
    }
  } else {
    const planConfig = PLANS[plan as keyof typeof PLANS];
    if (!planConfig) throw new Error(`Plan desconocido: ${plan}`);
    cfdiLimit = planConfig.cfdiLimit;
    usersLimit = planConfig.usersLimit;
  }

  // 1. Provision a dedicated database for this tenant
  const databaseName = await tenantDb.provisionDatabase(rfc);

  // 1b. Register tenant database in Metabase (non-blocking, logs errors only)
  metabaseService.registerDatabase({
    nombre: data.nombre,
    dbName: databaseName,
  }).catch(err => console.error('[METABASE] Register failed:', err));

  // 2. Create tenant record
  const tenant = await prisma.tenant.create({
    data: {
      nombre: data.nombre,
      rfc,
      plan: plan as any,
      databaseName,
      cfdiLimit,
      usersLimit,
      ...(data.verticalProfile && { verticalProfile: data.verticalProfile as any }),
      ...(dbMode && { dbMode: dbMode as any }),
      ...(trialEndsAt && { trialEndsAt }),
    }
  });

  // 3. Create admin user with temp password
  const tempPassword = randomBytes(4).toString('hex'); // 8-char random
  const hashedPassword = await bcrypt.hash(tempPassword, 10);

  // Get owner role ID from roles table (rol que asignamos al dueño del tenant al crearlo)
  const ownerRol = await prisma.rol.findUnique({ where: { nombre: 'owner' } });
  if (!ownerRol) throw new Error('Rol owner no encontrado en la base de datos');

  const user = await prisma.user.create({
    data: {
      email: data.adminEmail,
      passwordHash: hashedPassword,
      nombre: data.adminNombre,
      lastTenantId: tenant.id,
    },
  });

  // Crea membership owner del nuevo user en su tenant (fase 4 multi-tenant)
  await prisma.tenantMembership.create({
    data: {
      userId: user.id,
      tenantId: tenant.id,
      rolId: ownerRol.id,
      isOwner: true,
      active: true,
    },
  });

  // 4. Create timbre subscription for despacho plans (if applicable)
  if (isDespacho && timbresIncluidos > 0) {
    const inicio = new Date();
    const fin = new Date(inicio);
    fin.setMonth(fin.getMonth() + 1);
    fin.setDate(fin.getDate() - 1);
    await prisma.timbreSuscripcion.create({
      data: {
        tenantId: tenant.id,
        tipo: 'mensual',
        timbresLimite: timbresIncluidos,
        timbresUsados: 0,
        periodoInicio: inicio,
        periodoFin: fin,
      },
    });
  }

  // 5. Create subscription (only for paid plans)
  if (!isDespacho || (plan !== 'trial' && plan !== 'custom')) {
    await prisma.subscription.create({
      data: {
        tenantId: tenant.id,
        plan: plan as any,
        status: 'pending',
        amount: data.amount,
        frequency: data.frequency || 'monthly',
      },
    });
  }

  // 6. Send welcome email to client (non-blocking)
  emailService.sendWelcome(data.adminEmail, {
    nombre: data.adminNombre,
    email: data.adminEmail,
    tempPassword,
  }).catch(err => console.error('[EMAIL] Welcome email failed:', err));

  // 7. Send new client notification to admin with DB credentials
  emailService.sendNewClientAdmin({
    clienteNombre: data.nombre,
    clienteRfc: rfc,
    adminEmail: data.adminEmail,
    adminNombre: data.adminNombre,
    tempPassword,
    databaseName,
    plan,
  }).catch(err => console.error('[EMAIL] New client admin email failed:', err));

  return { tenant, user, tempPassword };
}

/**
 * Flow "Agregar empresa" — un user existente (típicamente owner) agrega un
 * segundo RFC bajo su cuenta. A diferencia de `createTenant()`, NO crea un user
 * nuevo: el caller se vuelve owner de la empresa nueva vía TenantMembership.
 *
 * Sin trial por default — el check de trial por owner (fase 5) bloquearía
 * cualquier intento de re-activarlo. El owner contrata el plan desde la página
 * de suscripción del nuevo tenant tras esta llamada.
 */
export async function addTenantToOwner(data: {
  userId: string;
  nombre: string;
  rfc: string;
  plan?: 'starter' | 'business' | 'business_ia' | 'enterprise';
}) {
  const plan = data.plan || 'starter';
  const planConfig = PLANS[plan];
  const rfcUpper = data.rfc.toUpperCase();

  // Valida que el RFC no exista en el sistema
  const existingTenant = await prisma.tenant.findUnique({ where: { rfc: rfcUpper } });
  if (existingTenant) {
    throw new Error('Ya existe una empresa con ese RFC en el sistema');
  }

  // Valida que el user exista y esté activo
  const user = await prisma.user.findUnique({ where: { id: data.userId } });
  if (!user || !user.active) throw new Error('Usuario no encontrado');

  // 1. Provision BD dedicada
  const databaseName = await tenantDb.provisionDatabase(rfcUpper);

  // 1b. Register tenant database in Metabase (non-blocking, logs errors only)
  metabaseService.registerDatabase({
    nombre: data.nombre,
    dbName: databaseName,
  }).catch(err => console.error('[METABASE] Register failed:', err));

  // 2. Crea el tenant
  const tenant = await prisma.tenant.create({
    data: {
      nombre: data.nombre,
      rfc: rfcUpper,
      plan,
      databaseName,
      cfdiLimit: planConfig.cfdiLimit,
      usersLimit: planConfig.usersLimit,
    },
  });

  // 3. Crea membership del caller como owner
  const ownerRol = await prisma.rol.findUnique({ where: { nombre: 'owner' } });
  if (!ownerRol) throw new Error('Rol owner no encontrado');

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

  // 4. Subscripción pending (se activa al contratar un plan)
  await prisma.subscription.create({
    data: {
      tenantId: tenant.id,
      plan,
      status: 'pending',
      amount: 0, // el precio real se setea al contratar
      frequency: 'monthly',
    },
  });

  return { tenant };
}

/**
 * Lista detallada de tenants del user actual con estado de suscripción. Usado
 * por la página `/mis-empresas` — incluye plan, status, currentPeriodEnd,
 * pendingPlan si aplica.
 *
 * @param onlyOwner filtra a solo memberships donde isOwner=true. Default true:
 * un user contador que trabaja en empresa ajena NO la ve aquí, pero sí sus
 * propias empresas donde es owner. El header switcher usa un endpoint distinto.
 */
export async function getMyTenantsDetailed(userId: string, onlyOwner = true) {
  const memberships = await prisma.tenantMembership.findMany({
    where: { userId, active: true, ...(onlyOwner ? { isOwner: true } : {}) },
    include: {
      tenant: {
        include: {
          subscriptions: {
            orderBy: { createdAt: 'desc' },
            take: 1,
          },
        },
      },
      rol: { select: { nombre: true } },
    },
    orderBy: { joinedAt: 'asc' },
  });

  return memberships
    .filter(m => m.tenant.active)
    .map(m => {
      const sub = m.tenant.subscriptions[0] || null;
      return {
        tenantId: m.tenant.id,
        nombre: m.tenant.nombre,
        rfc: m.tenant.rfc,
        plan: m.tenant.plan,
        role: m.rol.nombre,
        isOwner: m.isOwner,
        trialEndsAt: m.tenant.trialEndsAt,
        subscription: sub ? {
          status: sub.status,
          plan: sub.plan,
          amount: sub.amount ? Number(sub.amount) : 0,
          frequency: sub.frequency,
          currentPeriodEnd: sub.currentPeriodEnd,
          pendingPlan: sub.pendingPlan,
          pendingEffectiveAt: sub.pendingEffectiveAt,
        } : null,
      };
    });
}

export async function updateTenant(id: string, data: {
  nombre?: string;
  rfc?: string;
  plan?: string;
  verticalProfile?: 'CONTABLE' | 'JURIDICO' | 'ARQUITECTURA';
  active?: boolean;
}) {
  return prisma.tenant.update({
    where: { id },
    data: {
      ...(data.nombre && { nombre: data.nombre }),
      ...(data.rfc && { rfc: data.rfc.toUpperCase() }),
      ...(data.plan && { plan: data.plan as any }),
      ...(data.verticalProfile && { verticalProfile: data.verticalProfile as any }),
      ...(data.active !== undefined && { active: data.active }),
    },
    select: {
      id: true,
      nombre: true,
      rfc: true,
      plan: true,
      databaseName: true,
      cfdiLimit: true,
      usersLimit: true,
      active: true,
      createdAt: true,
    }
  });
}

export async function getDatosFiscales(id: string) {
  return prisma.tenant.findUnique({
    where: { id },
    select: {
      codigoPostal: true,
      calle: true,
      numExterior: true,
      numInterior: true,
      colonia: true,
      ciudad: true,
      municipio: true,
      estado: true,
      telefono: true,
    },
  });
}

export async function updateDatosFiscales(id: string, data: {
  codigoPostal?: string;
  calle?: string;
  numExterior?: string;
  numInterior?: string;
  colonia?: string;
  ciudad?: string;
  municipio?: string;
  estado?: string;
  telefono?: string;
}) {
  return prisma.tenant.update({
    where: { id },
    data,
    select: {
      codigoPostal: true,
      calle: true,
      numExterior: true,
      numInterior: true,
      colonia: true,
      ciudad: true,
      municipio: true,
      estado: true,
      telefono: true,
    },
  });
}

export async function deleteTenant(id: string) {
  const tenant = await prisma.tenant.findUnique({
    where: { id },
    select: { databaseName: true },
  });

  // Soft-delete the tenant record
  await prisma.tenant.update({
    where: { id },
    data: { active: false }
  });

  // Soft-delete the database (rename with _deleted_ suffix)
  if (tenant) {
    await tenantDb.deprovisionDatabase(tenant.databaseName);
    tenantDb.invalidatePool(id);
    // Remove from Metabase (non-blocking)
    metabaseService.deleteDatabase(tenant.databaseName).catch(err =>
      console.error('[METABASE] Delete failed:', err)
    );
  }
}
