import { prisma } from '../config/database.js';
import { emailService } from './email/email.service.js';
import { getTenantOwnerEmail } from '../utils/memberships.js';
import crypto from 'crypto';

function generateToken(): string {
  return crypto.randomBytes(32).toString('hex');
}

export async function createInvitation(params: {
  tenantId: string;
  invitedByUserId: string;
  plan?: string;
  durationDays: number;
}) {
  const tenant = await prisma.tenant.findUnique({
    where: { id: params.tenantId },
    select: { nombre: true, rfc: true, plan: true },
  });
  if (!tenant) throw new Error('Tenant no encontrado');

  // Verificar que no haya ya una invitación pendiente para este tenant
  const existingPending = await prisma.trialInvitation.findFirst({
    where: { tenantId: params.tenantId, status: 'pending' },
  });
  if (existingPending) {
    throw new Error('Este tenant ya tiene una invitación de trial pendiente');
  }

  // Verificar que el tenant no tenga ya una suscripción activa del mismo plan
  const existingSub = await prisma.subscription.findFirst({
    where: {
      tenantId: params.tenantId,
      status: { in: ['authorized', 'pending', 'trial'] },
      plan: (params.plan || 'business_control') as any,
    },
  });
  if (existingSub) {
    throw new Error(`Este tenant ya tiene una suscripción activa o en trial de ${params.plan || 'business_control'}`);
  }

  const token = generateToken();
  const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 días para aceptar

  const invitation = await prisma.trialInvitation.create({
    data: {
      tenantId: params.tenantId,
      invitedBy: params.invitedByUserId,
      plan: params.plan || 'business_control',
      durationDays: params.durationDays,
      token,
      expiresAt,
      emailSentTo: null,
    },
  });

  // Enviar email al owner (fire-and-forget)
  const ownerEmail = await getTenantOwnerEmail(params.tenantId);
  if (ownerEmail) {
    await prisma.trialInvitation.update({
      where: { id: invitation.id },
      data: { emailSentTo: ownerEmail },
    });
    const acceptUrl = `${process.env.FRONTEND_URL || 'https://app.horux360.com'}/invitacion/trial/${token}`;
    emailService.sendTrialInvitation(ownerEmail, {
      despachoNombre: tenant.nombre,
      plan: invitation.plan,
      durationDays: invitation.durationDays,
      acceptUrl,
      expiresAt: expiresAt.toLocaleDateString('es-MX'),
    }).catch((err: any) => console.error('[TrialInvitation] Email failed:', err.message));
  }

  return invitation;
}

export async function acceptInvitation(token: string, userId: string) {
  const invitation = await prisma.trialInvitation.findUnique({
    where: { token },
  });
  if (!invitation) throw new Error('Invitación no encontrada');
  if (invitation.status !== 'pending') throw new Error(`Invitación ya ${invitation.status}`);
  if (invitation.expiresAt < new Date()) {
    await prisma.trialInvitation.update({
      where: { id: invitation.id },
      data: { status: 'expired' },
    });
    throw new Error('La invitación ha expirado');
  }

  // Verificar que el usuario sea owner del tenant
  const membership = await prisma.tenantMembership.findFirst({
    where: { userId, tenantId: invitation.tenantId, isOwner: true, active: true },
  });
  if (!membership) {
    throw new Error('Solo el dueño del despacho puede aceptar esta invitación');
  }

  const trialEndsAt = new Date();
  trialEndsAt.setDate(trialEndsAt.getDate() + invitation.durationDays);
  const now = new Date();

  await prisma.$transaction(async (tx) => {
    // Actualizar tenant
    await tx.tenant.update({
      where: { id: invitation.tenantId },
      data: {
        plan: invitation.plan as any,
        trialEndsAt,
      },
    });

    // Cancelar cualquier subscription trial anterior genérica
    await tx.subscription.updateMany({
      where: { tenantId: invitation.tenantId, status: 'trial' },
      data: { status: 'trial_converted' },
    });

    // Crear nueva subscription de trial con el plan específico
    await tx.subscription.create({
      data: {
        tenantId: invitation.tenantId,
        plan: invitation.plan as any,
        status: 'trial',
        amount: 0,
        frequency: 'annual',
        currentPeriodStart: now,
        currentPeriodEnd: trialEndsAt,
      },
    });

    // Marcar invitación como aceptada
    await tx.trialInvitation.update({
      where: { id: invitation.id },
      data: { status: 'accepted', acceptedAt: now },
    });
  });

  return { success: true, trialEndsAt, plan: invitation.plan, durationDays: invitation.durationDays };
}

export async function getInvitations(filters?: { tenantId?: string; status?: string }) {
  const where: any = {};
  if (filters?.tenantId) where.tenantId = filters.tenantId;
  if (filters?.status) where.status = filters.status;

  const invitations = await prisma.trialInvitation.findMany({
    where,
    orderBy: { createdAt: 'desc' },
  });

  // Enrich with tenant data
  const tenantIds = [...new Set(invitations.map(i => i.tenantId))];
  const tenants = await prisma.tenant.findMany({
    where: { id: { in: tenantIds } },
    select: { id: true, nombre: true, rfc: true },
  });
  const tenantMap = new Map(tenants.map(t => [t.id, t]));

  return invitations.map(inv => ({
    ...inv,
    tenant: tenantMap.get(inv.tenantId) || null,
  }));
}

export async function getPendingInvitationForTenant(tenantId: string) {
  const invitation = await prisma.trialInvitation.findFirst({
    where: { tenantId, status: 'pending', expiresAt: { gt: new Date() } },
  });
  if (!invitation) return null;

  const tenant = await prisma.tenant.findUnique({
    where: { id: tenantId },
    select: { nombre: true, rfc: true },
  });

  return { ...invitation, tenant };
}

export async function cancelInvitation(invitationId: string) {
  const invitation = await prisma.trialInvitation.findUnique({
    where: { id: invitationId },
  });
  if (!invitation) throw new Error('Invitación no encontrada');
  if (invitation.status !== 'pending') throw new Error('Solo se pueden cancelar invitaciones pendientes');

  return prisma.trialInvitation.update({
    where: { id: invitationId },
    data: { status: 'cancelled' },
  });
}
