/**
 * Billing & Subscriptions — Service Layer
 * ------------------------------------------
 * Manages plans, subscriptions, and usage tracking.
 *
 * How it works:
 *   1. Every org gets a Subscription (defaults to the "free" plan)
 *   2. OrgPlanUsage tracks current usage (brands, websites, events, users)
 *   3. checkLimit() compares usage against the plan's limits
 *   4. No Stripe yet — status changes are manual or mock
 *
 * Price is stored in cents: 4900 = $49.00
 */

import { prisma } from "@/lib/prisma";

// ─── Types ───────────────────────────────────────────────────────────────────

export interface PlanInfo {
  id: string;
  name: string;
  slug: string;
  priceMonthly: number;
  billingInterval: string;
  features: Record<string, boolean>;
  limits: { brands: number; websites: number; events: number; users: number };
}

export interface SubscriptionInfo {
  id: string;
  status: string;
  plan: PlanInfo;
  startDate: string;
  endDate: string | null;
  trialEnd: string | null;
  trialDaysLeft: number | null;
}

export interface UsageInfo {
  brandsUsed: number;
  brandsLimit: number;
  websitesUsed: number;
  websitesLimit: number;
  eventsUsed: number;
  eventsLimit: number;
  usersUsed: number;
  usersLimit: number;
  periodStart: string;
}

export interface BillingOverview {
  subscription: SubscriptionInfo | null;
  usage: UsageInfo | null;
  plans: PlanInfo[];
}

// ─── Get all available plans ─────────────────────────────────────────────────

export async function getPlans(): Promise<PlanInfo[]> {
  const plans = await prisma.plan.findMany({
    where: { isActive: true },
    orderBy: { sortOrder: "asc" },
  });

  return plans.map(formatPlan);
}

// ─── Get or create subscription for an org ───────────────────────────────────

export async function getSubscription(orgId: string): Promise<SubscriptionInfo | null> {
  const sub = await prisma.subscription.findUnique({
    where: { orgId },
    include: { plan: true },
  });

  if (!sub) return null;

  const trialDaysLeft = sub.trialEnd
    ? Math.max(0, Math.ceil((sub.trialEnd.getTime() - Date.now()) / 86400000))
    : null;

  return {
    id: sub.id,
    status: sub.status,
    plan: formatPlan(sub.plan),
    startDate: sub.startDate.toISOString(),
    endDate: sub.endDate?.toISOString() ?? null,
    trialEnd: sub.trialEnd?.toISOString() ?? null,
    trialDaysLeft,
  };
}

/**
 * Ensure an org has a subscription. If none exists, create one
 * on the free plan with a 14-day trial.
 */
export async function ensureSubscription(orgId: string): Promise<SubscriptionInfo> {
  const existing = await getSubscription(orgId);
  if (existing) return existing;

  // Find the free plan
  let freePlan = await prisma.plan.findUnique({ where: { slug: "free" } });

  // If no plans exist yet, create the defaults
  if (!freePlan) {
    await seedDefaultPlans();
    freePlan = await prisma.plan.findUnique({ where: { slug: "free" } });
  }

  if (!freePlan) throw new Error("Free plan not found — run seed-plans");

  const trialEnd = new Date();
  trialEnd.setDate(trialEnd.getDate() + 14);

  const sub = await prisma.subscription.create({
    data: {
      orgId,
      planId: freePlan.id,
      status: "trial",
      trialEnd,
    },
  });

  // Also create a usage record
  await prisma.orgPlanUsage.upsert({
    where: { orgId },
    create: { orgId },
    update: {},
  });

  return (await getSubscription(orgId))!;
}

// ─── Change plan ─────────────────────────────────────────────────────────────

export async function changePlan(orgId: string, planSlug: string): Promise<SubscriptionInfo | null> {
  const plan = await prisma.plan.findUnique({ where: { slug: planSlug } });
  if (!plan) return null;

  await prisma.subscription.upsert({
    where: { orgId },
    create: {
      orgId,
      planId: plan.id,
      status: "active",
    },
    update: {
      planId: plan.id,
      status: "active",
      trialEnd: null,
    },
  });

  return getSubscription(orgId);
}

// ─── Cancel subscription ─────────────────────────────────────────────────────

export async function cancelSubscription(orgId: string): Promise<SubscriptionInfo | null> {
  const sub = await prisma.subscription.findUnique({ where: { orgId } });
  if (!sub) return null;

  await prisma.subscription.update({
    where: { orgId },
    data: {
      status: "canceled",
      canceledAt: new Date(),
      endDate: sub.trialEnd ?? new Date(Date.now() + 30 * 86400000), // end of current period
    },
  });

  return getSubscription(orgId);
}

// ─── Usage tracking ──────────────────────────────────────────────────────────

/**
 * Recalculate usage for an org by counting actual resources.
 * Call this periodically or after resource changes.
 */
export async function recalculateUsage(orgId: string): Promise<UsageInfo | null> {
  const sub = await prisma.subscription.findUnique({
    where: { orgId },
    include: { plan: true },
  });
  if (!sub) return null;

  // Count actual resources
  const brandsUsed = await prisma.brand.count({ where: { orgId } });
  const usersUsed = await prisma.orgMembership.count({ where: { orgId } });

  // Count websites (all brands' websites)
  const websitesUsed = await prisma.website.count({
    where: { brand: { orgId } },
  });

  // Count tracked events this billing period
  const usage = await prisma.orgPlanUsage.findUnique({ where: { orgId } });
  const periodStart = usage?.periodStart ?? sub.startDate;

  const eventsUsed = await prisma.trackedEvent.count({
    where: {
      trackedSite: { orgId },
      timestamp: { gte: periodStart },
    },
  });

  // Update the usage record
  await prisma.orgPlanUsage.upsert({
    where: { orgId },
    create: { orgId, brandsUsed, websitesUsed, eventsUsed, usersUsed, periodStart },
    update: { brandsUsed, websitesUsed, eventsUsed, usersUsed, lastCalculatedAt: new Date() },
  });

  return {
    brandsUsed,
    brandsLimit: sub.plan.limitBrands,
    websitesUsed,
    websitesLimit: sub.plan.limitWebsites,
    eventsUsed,
    eventsLimit: sub.plan.limitEvents,
    usersUsed,
    usersLimit: sub.plan.limitUsers,
    periodStart: periodStart.toISOString(),
  };
}

// ─── Limit checks ────────────────────────────────────────────────────────────

export type LimitType = "brands" | "websites" | "events" | "users";

export interface LimitCheck {
  allowed: boolean;
  current: number;
  limit: number;
  remaining: number;
}

/**
 * Check if an org has room for one more of a given resource.
 * Returns { allowed, current, limit, remaining }.
 */
export async function checkLimit(orgId: string, limitType: LimitType): Promise<LimitCheck> {
  const sub = await prisma.subscription.findUnique({
    where: { orgId },
    include: { plan: true },
  });

  if (!sub) return { allowed: true, current: 0, limit: 999, remaining: 999 };

  const usage = await recalculateUsage(orgId);
  if (!usage) return { allowed: true, current: 0, limit: 999, remaining: 999 };

  const map: Record<LimitType, { current: number; limit: number }> = {
    brands:   { current: usage.brandsUsed,   limit: usage.brandsLimit },
    websites: { current: usage.websitesUsed, limit: usage.websitesLimit },
    events:   { current: usage.eventsUsed,   limit: usage.eventsLimit },
    users:    { current: usage.usersUsed,    limit: usage.usersLimit },
  };

  const { current, limit } = map[limitType];
  return {
    allowed: current < limit,
    current,
    limit,
    remaining: Math.max(0, limit - current),
  };
}

// ─── Check feature flag ──────────────────────────────────────────────────────

export async function hasFeature(orgId: string, featureName: string): Promise<boolean> {
  const sub = await prisma.subscription.findUnique({
    where: { orgId },
    include: { plan: true },
  });
  if (!sub) return false;

  const features = (sub.plan.features as Record<string, boolean>) ?? {};
  return features[featureName] === true;
}

// ─── Full billing overview ───────────────────────────────────────────────────

export async function getBillingOverview(orgId: string): Promise<BillingOverview> {
  const subscription = await ensureSubscription(orgId);
  const usage = await recalculateUsage(orgId);
  const plans = await getPlans();

  return { subscription, usage, plans };
}

// ─── Seed default plans ──────────────────────────────────────────────────────

export async function seedDefaultPlans(): Promise<void> {
  const plans = [
    {
      name: "Free", slug: "free", priceMonthly: 0, sortOrder: 0,
      limitBrands: 1, limitWebsites: 1, limitEvents: 1000, limitUsers: 1,
      features: { dashboard: true, technical_audit: true, site_tag: true },
    },
    {
      name: "Starter", slug: "starter", priceMonthly: 4900, sortOrder: 1,
      limitBrands: 3, limitWebsites: 3, limitEvents: 10000, limitUsers: 3,
      features: { dashboard: true, technical_audit: true, site_tag: true, content_briefs: true, competitors: true, ai_avatar: true },
    },
    {
      name: "Pro", slug: "pro", priceMonthly: 14900, sortOrder: 2,
      limitBrands: 10, limitWebsites: 10, limitEvents: 100000, limitUsers: 10,
      features: { dashboard: true, technical_audit: true, site_tag: true, content_briefs: true, competitors: true, ai_avatar: true, conversion_intelligence: true, local_geo: true, report_studio: true },
    },
    {
      name: "Enterprise", slug: "enterprise", priceMonthly: 49900, sortOrder: 3,
      limitBrands: 100, limitWebsites: 100, limitEvents: 1000000, limitUsers: 50,
      features: { dashboard: true, technical_audit: true, site_tag: true, content_briefs: true, competitors: true, ai_avatar: true, conversion_intelligence: true, local_geo: true, report_studio: true, api_access: true, white_label: true },
    },
  ];

  for (const p of plans) {
    await prisma.plan.upsert({
      where: { slug: p.slug },
      create: { ...p, features: p.features },
      update: { ...p, features: p.features },
    });
  }
}

// ─── Helpers ─────────────────────────────────────────────────────────────────

function formatPlan(plan: {
  id: string; name: string; slug: string; priceMonthly: number;
  billingInterval: string; features: unknown;
  limitBrands: number; limitWebsites: number; limitEvents: number; limitUsers: number;
}): PlanInfo {
  return {
    id: plan.id,
    name: plan.name,
    slug: plan.slug,
    priceMonthly: plan.priceMonthly,
    billingInterval: plan.billingInterval,
    features: (plan.features as Record<string, boolean>) ?? {},
    limits: {
      brands: plan.limitBrands,
      websites: plan.limitWebsites,
      events: plan.limitEvents,
      users: plan.limitUsers,
    },
  };
}
