import crypto from 'crypto';
import { PrismaClient } from '@prisma/client';
import type { TranslationExecutionMode } from '@prisma/client';
import { hashPassword } from '../utils/encryption';

// Keep this as a local type to avoid coupling compilation to
// a freshly regenerated Prisma client during development.
export type LicenseStatus = 'active' | 'suspended' | 'expired' | 'revoked';

export type PluginTier = 'personal' | 'professional' | 'agency';

export type MultilingualLicenseInfo = {
  // Multilingual Press Zone plugin tier semantics.
  // Stored plan_tier is a string slug, but plugin expects personal/professional/agency.
  tier: PluginTier;
  status: LicenseStatus;
  sites_allowed: number;
  sites_used: number;
  languages_allowed: number;
  expires_at: string;
};

const prisma = new PrismaClient();

function mapPlanTierToPluginTier(planTier: string): PluginTier {
  switch (planTier) {
    case 'starter':
      return 'personal';
    case 'professional':
      return 'professional';
    case 'enterprise':
      return 'agency';
    default: {
      // Fallback for unknown tier slugs.
      return 'personal';
    }
  }
}

export function hashLicenseKey(licenseKey: string): string {
  // SHA-256; never store plain keys.
  return crypto.createHash('sha256').update(licenseKey).digest('hex');
}

export function generateLicenseKey(prefix: string = 'MULT'): string {
  // Key format: PREFIX-XXXX-XXXX-XXXX (A-Z0-9)
  const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  const bytes = crypto.randomBytes(12); // 12 random bytes for 3 groups
  const chars = Array.from(bytes, (b) => alphabet[b % alphabet.length]);
  const raw = chars.join('');
  return `${prefix}-${raw.slice(0, 4)}-${raw.slice(4, 8)}-${raw.slice(8, 12)}`;
}

export function normalizeSiteUrl(siteUrl: string): string {
  // Store the full origin + path? For licensing we want a stable identifier.
  // Use origin if possible, else fall back to raw string.
  try {
    const u = new URL(siteUrl);
    return u.origin;
  } catch {
    return siteUrl.trim();
  }
}

/** Map plugin name to license key prefix. */
function pluginToPrefix(plugin: string): string {
  switch (plugin) {
    case 'translate':
      return 'TRAN';
    case 'international':
      return 'INTL';
    case 'multilingual':
    default:
      return 'MULT';
  }
}

export async function createLicense(params: {
  plan_tier: string;
  sites_allowed: number;
  expires_at: Date;
  languages_allowed?: number;
  plugin?: string;
  user_id?: string;
  execution_mode?: TranslationExecutionMode;
}): Promise<{ license_key: string; license: { id: string; key_last4: string; execution_mode: TranslationExecutionMode } }>
{
  const plugin = params.plugin ?? 'multilingual';
  const prefix = pluginToPrefix(plugin);
  const license_key = generateLicenseKey(prefix);
  const key_hash = hashLicenseKey(license_key);
  const key_last4 = license_key.replace(/-/g, '').slice(-4);

  const languages_allowed = params.languages_allowed ?? (() => {
    switch (params.plan_tier) {
      case 'starter': return 1;
      case 'professional': return 5;
      case 'enterprise': return -1;
      default: return 1;
    }
  })();

  const created = await prisma.license.create({
    data: {
      key_hash,
      key_last4,
      user_id: params.user_id,
      plugin,
      plan_tier: params.plan_tier,
      sites_allowed: params.sites_allowed,
      languages_allowed,
      expires_at: params.expires_at,
      status: 'active',
      execution_mode: params.execution_mode ?? 'live',
    },
    select: {
      id: true,
      key_last4: true,
      execution_mode: true,
    },
  });

  return { license_key, license: created };
}

export async function ensureLicensePrincipal(params: {
  license_key: string;
  plugin: string;
}): Promise<{ user_id: string }> {
  const key_hash = hashLicenseKey(params.license_key);
  const license = await prisma.license.findUnique({
    where: { key_hash },
    include: {
      user: { include: { subscriptions: true } },
    },
  });

  if (!license || license.plugin !== params.plugin) {
    throw new Error('INVALID_LICENSE');
  }
  if (license.expires_at.getTime() < Date.now()) {
    throw new Error('LICENSE_EXPIRED');
  }
  if (license.status !== 'active') {
    throw new Error('LICENSE_NOT_ACTIVE');
  }

  if (license.user_id && license.user) {
    const activeSubscription = license.user.subscriptions.find(
      subscription => subscription.plugin === params.plugin && subscription.status === 'active'
    );
    if (license.user.status !== 'active' || !activeSubscription) {
      throw new Error('SUBSCRIPTION_REQUIRED');
    }
    return { user_id: license.user_id };
  }

  const email = `license_${key_hash.slice(0, 56)}@licenses.press.zone`;
  const password_hash = await hashPassword(crypto.randomBytes(32).toString('hex'));

  return prisma.$transaction(async transaction => {
    const user = await transaction.user.upsert({
      where: { email },
      update: { status: 'active', email_verified: true },
      create: {
        email,
        password_hash,
        email_verified: true,
        status: 'active',
      },
    });

    await transaction.subscription.upsert({
      where: {
        user_id_plugin: {
          user_id: user.id,
          plugin: params.plugin,
        },
      },
      update: {
        plan_tier: license.plan_tier,
        status: 'active',
        current_period_end: license.expires_at,
      },
      create: {
        user_id: user.id,
        plugin: params.plugin,
        plan_tier: license.plan_tier,
        billing_cycle: 'annual',
        status: 'active',
        current_period_start: new Date(),
        current_period_end: license.expires_at,
      },
    });

    const linked = await transaction.license.updateMany({
      where: { id: license.id, user_id: null },
      data: { user_id: user.id },
    });
    if (linked.count === 0) {
      const current = await transaction.license.findUnique({
        where: { id: license.id },
        select: { user_id: true },
      });
      if (current?.user_id !== user.id) {
        throw new Error('LICENSE_OWNER_AMBIGUOUS');
      }
    }

    return { user_id: user.id };
  });
}

export async function activateLicense(params: {
  license_key: string;
  site_url: string;
  plugin?: string;
}): Promise<MultilingualLicenseInfo> {
  const key_hash = hashLicenseKey(params.license_key);
  const site_url = normalizeSiteUrl(params.site_url);

  await ensureLicensePrincipal({
    license_key: params.license_key,
    plugin: params.plugin ?? 'multilingual',
  });

  const license = await prisma.license.findUnique({
    where: { key_hash },
    include: {
      activations: {
        where: { deactivated_at: null },
        select: { id: true, site_url: true },
      },
    },
  });

  if (!license) {
    throw new Error('INVALID_LICENSE');
  }

  // Verify the license belongs to the expected plugin
  if (params.plugin && license.plugin !== params.plugin) {
    throw new Error('INVALID_LICENSE');
  }

  // Expiry check
  if (license.expires_at.getTime() < Date.now()) {
    // Mark status as expired (best-effort)
    await prisma.license.update({
      where: { id: license.id },
      data: { status: 'expired' },
    }).catch(() => undefined);
    throw new Error('LICENSE_EXPIRED');
  }

  if (license.status !== 'active') {
    throw new Error('LICENSE_NOT_ACTIVE');
  }

  // Already activated on this site (active activations only)
  const existing = license.activations.find((a: { site_url: string }) => a.site_url === site_url);
  if (!existing) {
    const used = license.activations.length;
    if (license.sites_allowed !== -1 && used >= license.sites_allowed) {
      throw new Error('SITE_LIMIT_REACHED');
    }

    // Use upsert to handle re-activation after deactivation
    // (unique constraint on license_id + site_url prevents duplicate rows)
    await prisma.licenseActivation.upsert({
      where: {
        license_id_site_url: {
          license_id: license.id,
          site_url,
        },
      },
      update: {
        deactivated_at: null,
      },
      create: {
        license_id: license.id,
        site_url,
      },
    });
  }

  const sites_used = await prisma.licenseActivation.count({
    where: { license_id: license.id, deactivated_at: null },
  });

  return {
    tier: mapPlanTierToPluginTier(license.plan_tier),
    status: license.status,
    sites_allowed: license.sites_allowed,
    sites_used,
    languages_allowed: license.languages_allowed,
    expires_at: license.expires_at.toISOString(),
  };
}

export async function deactivateLicense(params: {
  license_key: string;
  site_url: string;
  plugin?: string;
}): Promise<{ success: true }>
{
  const key_hash = hashLicenseKey(params.license_key);
  const site_url = normalizeSiteUrl(params.site_url);

  const license = await prisma.license.findUnique({
    where: { key_hash },
    select: { id: true, plugin: true },
  });

  if (!license) {
    throw new Error('INVALID_LICENSE');
  }

  // Verify the license belongs to the expected plugin
  if (params.plugin && license.plugin !== params.plugin) {
    throw new Error('INVALID_LICENSE');
  }

  await prisma.licenseActivation.updateMany({
    where: {
      license_id: license.id,
      site_url,
      deactivated_at: null,
    },
    data: { deactivated_at: new Date() },
  });

  return { success: true };
}

// ---------------------------------------------------------------------------
// Admin CRUD & Lifecycle
// ---------------------------------------------------------------------------

export interface ListLicensesParams {
  page?: number;
  per_page?: number;
  plugin?: string;
  status?: string;
  plan_tier?: string;
  key_last4?: string;
  sort_by?: string;
  sort_order?: 'asc' | 'desc';
}

export async function listLicenses(params: ListLicensesParams) {
  const page = Math.max(1, params.page ?? 1);
  const limit = Math.min(100, Math.max(1, params.per_page ?? 25));
  const sortBy = params.sort_by ?? 'created_at';
  const sortOrder = params.sort_order ?? 'desc';

  const where: Record<string, any> = {};
  if (params.plugin) where.plugin = params.plugin;
  if (params.status) where.status = params.status;
  if (params.plan_tier) where.plan_tier = params.plan_tier;
  if (params.key_last4) where.key_last4 = { contains: params.key_last4 };

  const [total, data] = await Promise.all([
    prisma.license.count({ where }),
    prisma.license.findMany({
      where,
      orderBy: { [sortBy]: sortOrder },
      skip: (page - 1) * limit,
      take: limit,
      select: {
        id: true,
        key_last4: true,
        plugin: true,
        plan_tier: true,
        status: true,
        execution_mode: true,
        sites_allowed: true,
        languages_allowed: true,
        expires_at: true,
        created_at: true,
        _count: { select: { activations: true } },
      },
    }),
  ]);

  const totalPages = Math.ceil(total / limit);

  return {
    data,
    pagination: {
      page,
      limit,
      total,
      totalPages,
      hasNext: page < totalPages,
      hasPrev: page > 1,
    },
  };
}

export async function getLicenseById(id: string) {
  const license = await prisma.license.findUnique({
    where: { id },
    include: {
      activations: {
        orderBy: { activated_at: 'desc' },
      },
    },
  });

  if (!license) {
    throw new Error('LICENSE_NOT_FOUND');
  }

  return license;
}

export async function updateLicenseStatus(id: string, status: LicenseStatus) {
  const license = await prisma.license.findUnique({ where: { id } });

  if (!license) {
    throw new Error('LICENSE_NOT_FOUND');
  }

  if (license.status === 'revoked') {
    throw new Error('CANNOT_REACTIVATE_REVOKED');
  }

  return prisma.license.update({
    where: { id },
    data: { status },
  });
}

export async function extendLicense(id: string, newExpiresAt: Date) {
  const license = await prisma.license.findUnique({ where: { id } });

  if (!license) {
    throw new Error('LICENSE_NOT_FOUND');
  }

  const data: Record<string, any> = { expires_at: newExpiresAt };

  // Auto-reactivate if expired and new date is in the future
  if (license.status === 'expired' && newExpiresAt.getTime() > Date.now()) {
    data.status = 'active';
  }

  return prisma.license.update({ where: { id }, data });
}

export async function updateLicenseLimits(
  id: string,
  limits: { sites_allowed?: number; languages_allowed?: number }
) {
  if (limits.sites_allowed === undefined && limits.languages_allowed === undefined) {
    throw new Error('NO_FIELDS_TO_UPDATE');
  }

  const license = await prisma.license.findUnique({ where: { id } });

  if (!license) {
    throw new Error('LICENSE_NOT_FOUND');
  }

  const data: Record<string, number> = {};
  if (limits.sites_allowed !== undefined) data.sites_allowed = limits.sites_allowed;
  if (limits.languages_allowed !== undefined) data.languages_allowed = limits.languages_allowed;

  return prisma.license.update({ where: { id }, data });
}

export async function regenerateLicenseKey(id: string) {
  const license = await prisma.license.findUnique({ where: { id } });

  if (!license) {
    throw new Error('LICENSE_NOT_FOUND');
  }

  const prefix = pluginToPrefix(license.plugin);
  const license_key = generateLicenseKey(prefix);
  const key_hash = hashLicenseKey(license_key);
  const key_last4 = license_key.replace(/-/g, '').slice(-4);

  // Deactivate all existing site activations (old key is dead)
  await prisma.licenseActivation.updateMany({
    where: { license_id: id, deactivated_at: null },
    data: { deactivated_at: new Date() },
  });

  await prisma.license.update({
    where: { id },
    data: { key_hash, key_last4, status: 'active' },
  });

  return { license_key, key_last4 };
}

export async function adminDeactivateSite(licenseId: string, activationId: string) {
  const activation = await prisma.licenseActivation.findFirst({
    where: { id: activationId, license_id: licenseId },
  });

  if (!activation) {
    throw new Error('ACTIVATION_NOT_FOUND');
  }

  return prisma.licenseActivation.update({
    where: { id: activationId },
    data: { deactivated_at: new Date() },
  });
}

export async function validateLicense(params: {
  license_key: string;
  site_url: string;
  plugin?: string;
}): Promise<MultilingualLicenseInfo> {
  // Validation is activation-safe: it does not create new activations.
  const key_hash = hashLicenseKey(params.license_key);
  const license = await prisma.license.findUnique({
    where: { key_hash },
    include: {
      activations: {
        where: { deactivated_at: null },
        select: { site_url: true },
      },
    },
  });

  if (!license) {
    throw new Error('INVALID_LICENSE');
  }

  // Verify the license belongs to the expected plugin
  if (params.plugin && license.plugin !== params.plugin) {
    throw new Error('INVALID_LICENSE');
  }

  const sites_used = license.activations.length;

  // If expired, reflect it.
  const expired = license.expires_at.getTime() < Date.now();
  const status: LicenseStatus = expired ? 'expired' : license.status;

  // If not activated on this site, still return info; plugin may choose to block.
  // (We intentionally do not error here.)

  return {
    tier: mapPlanTierToPluginTier(license.plan_tier),
    status,
    sites_allowed: license.sites_allowed,
    sites_used,
    languages_allowed: license.languages_allowed,
    expires_at: license.expires_at.toISOString(),
  };
}
