import crypto from 'node:crypto';
import prisma from '../config/db.js';
import { env } from '../config/env.js';
import { ORG_ADMIN_ROLE } from '../config/access.js';
import { sendTemplateEmail } from '../utils/mailer.js';

// The "valid for 7 days" figure shown in blenaxis-super-admin's wizard copy —
// not represented anywhere in that frontend's code, so this backend owns it.
// Separate from invite.model.ts, which is the tenant app's own "invite your
// employees" feature (User.inviteToken-based) — this one is the Super
// Admin onboarding wizard's single invite for a tenant's first Organization Admin.
export const INVITE_TTL_DAYS = 7;

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

// The tenant app's invite page (/invite/:token) opens these links as well as a
// tenant's own employee invites; the backend's /invites routes resolve both.
export const inviteUrl = (token: string) => `${env.TENANT_APP_URL}/invite/${token}`;

export const isInviteUsable = (invite: { status: string; expiresAt: Date }) =>
  invite.status === 'pending' && invite.expiresAt > new Date();

export function createInvite(organizationId: number, data: { name: string; email: string }, invitedById: number | undefined) {
  const expiresAt = new Date(Date.now() + INVITE_TTL_DAYS * 24 * 60 * 60 * 1000);
  return prisma.organizationInvite.create({
    data: { organizationId, ...data, token: generateToken(), invitedById, expiresAt },
  });
}

// Superseding an old pending invite (resend / re-invite a different person)
// revokes it rather than deleting it, so history is kept.
export function revokePendingInvites(organizationId: number) {
  return prisma.organizationInvite.updateMany({
    where: { organizationId, status: 'pending' },
    data: { status: 'revoked' },
  });
}

export function getLatestInvite(organizationId: number) {
  return prisma.organizationInvite.findFirst({ where: { organizationId }, orderBy: { invitedAt: 'desc' } });
}

export function findInviteByToken(token: string) {
  return prisma.organizationInvite.findUnique({ where: { token }, include: { organization: true } });
}

export function markAccepted(id: number, createdUserId: number) {
  return prisma.organizationInvite.update({
    where: { id },
    data: { status: 'accepted', acceptedAt: new Date(), createdUserId },
  });
}

const displayDate = (date: Date) => new Intl.DateTimeFormat('en-IN', { dateStyle: 'medium' }).format(date);

// "Set your password & activate your account" email for a new organization's
// first Organization Admin — sent when the Super Admin onboards the
// organization, reassigns its admin, or resends the invite.
export function sendOrganizationAdminInviteEmail(invite: { name: string; email: string; token: string; expiresAt: Date }, organizationName: string) {
  return sendTemplateEmail({
    to: invite.email,
    subject: `Activate ${organizationName} on BlenAxis`,
    template: env.MAILGUN_TEMPLATE_ORGANIZATION_ADMIN_INVITE,
    variables: {
      name: invite.name,
      organizationName,
      inviteUrl: inviteUrl(invite.token),
      expiresAt: displayDate(invite.expiresAt),
      supportEmail: env.SUPPORT_EMAIL,
    },
  });
}

// Creates the invited person as the organization's Organization Admin: the tenant
// role (user_roles) is what gives them access in the tenant app.
export async function acceptOrganizationInvite(
  invite: { id: number; organizationId: number; email: string; name: string },
  firebaseUid: string,
  name: string,
) {
  const role = await prisma.role.findUnique({ where: { name: ORG_ADMIN_ROLE } });
  if (!role) throw new Error(`Role ${ORG_ADMIN_ROLE} is missing; run the seed.`);
  // One nested write, so the invite is never marked accepted without its user (or the reverse).
  const { createdUser } = await prisma.organizationInvite.update({
    where: { id: invite.id },
    data: {
      status: 'accepted',
      acceptedAt: new Date(),
      createdUser: {
        create: {
          firebaseUid,
          email: invite.email,
          name,
          organizationId: invite.organizationId,
          roleId: role.id,
          emailVerified: true,
          lastLoginAt: new Date(),
          tenantRoles: { create: { roleId: role.id } },
        },
      },
    },
    include: { createdUser: { include: { role: true } } },
  });
  return createdUser!;
}
