import { prisma } from '../../config/database';
import { getStripe, getPublishableKey } from '../../services/stripe.client';
import { env } from '../../config/env';
import { logger } from '../../utils/logger';
import type { SubscriptionStatus, Prisma } from '@prisma/client';
import { stringify } from 'csv-stringify/sync';

/** Sanitize a string to prevent CSV formula injection */
function sanitizeCsvValue(value: string): string {
  if (!value) return value;
  const dangerous = ['=', '+', '-', '@', '\t', '\r'];
  if (dangerous.some(c => value.startsWith(c))) {
    return `'${value}`;
  }
  return value;
}

export const subscriptionsService = {
  /** Create a Stripe Checkout session for a subscription */
  async createCheckoutSession(userId: string, planId: number, frequency: 'monthly' | 'yearly') {
    const stripe = await getStripe();
    const plan = await prisma.subscriptionPlan.findUnique({ where: { id: planId } });
    if (!plan || !plan.isActive) throw new Error('Plan not found or inactive');

    const priceId = frequency === 'yearly' ? plan.stripeYearlyPriceId : plan.stripePriceId;
    if (!priceId) throw new Error(`Plan has no Stripe ${frequency} price ID configured`);

    const user = await prisma.user.findUnique({ where: { id: userId } });
    if (!user) throw new Error('User not found');

    // Check for existing active sub
    const existing = await prisma.userSubscription.findFirst({
      where: { userId, status: 'active' },
    });
    if (existing) throw new Error('User already has an active subscription');

    // Find or create Stripe customer
    let customerId: string | undefined;
    const existingSub = await prisma.userSubscription.findFirst({
      where: { userId, stripeCustomerId: { not: null } },
      orderBy: { createdAt: 'desc' },
    });

    if (existingSub?.stripeCustomerId) {
      customerId = existingSub.stripeCustomerId;
    } else {
      const customer = await stripe.customers.create({
        email: user.email,
        name: user.name || undefined,
        metadata: { userId },
      });
      customerId = customer.id;
    }

    const session = await stripe.checkout.sessions.create({
      mode: 'subscription',
      customer: customerId,
      line_items: [{ price: priceId, quantity: 1 }],
      success_url: `${env.ADMIN_URL}/payments/success?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${env.ADMIN_URL}/pricing`,
      metadata: {
        type: 'subscription',
        userId,
        planId: planId.toString(),
        frequency,
      },
    });

    return { sessionId: session.id, url: session.url };
  },

  /** Cancel a subscription (at period end by default) */
  async cancelSubscription(subscriptionId: number, immediate = false) {
    const sub = await prisma.userSubscription.findUnique({ where: { id: subscriptionId } });
    if (!sub) throw new Error('Subscription not found');

    if (sub.stripeSubscriptionId) {
      const stripe = await getStripe();
      if (immediate) {
        await stripe.subscriptions.cancel(sub.stripeSubscriptionId);
      } else {
        await stripe.subscriptions.update(sub.stripeSubscriptionId, {
          cancel_at_period_end: true,
        });
      }
    }

    return prisma.userSubscription.update({
      where: { id: subscriptionId },
      data: {
        cancelAtPeriodEnd: !immediate,
        cancelledAt: immediate ? new Date() : undefined,
        status: immediate ? 'cancelled' : undefined,
      },
    });
  },

  /** Get active subscription for a user */
  async getActiveSubscription(userId: string) {
    return prisma.userSubscription.findFirst({
      where: {
        userId,
        status: { in: ['active', 'grace_period'] },
      },
      include: { plan: true },
      orderBy: { createdAt: 'desc' },
    });
  },

  /** List all subscriptions (admin) */
  async listSubscriptions(filters: {
    page: number;
    limit: number;
    status?: SubscriptionStatus;
    planId?: number;
    search?: string;
  }) {
    const { page, limit, status, planId, search } = filters;
    const where: Prisma.UserSubscriptionWhereInput = {};

    if (status) where.status = status;
    if (planId) where.planId = planId;
    if (search) {
      where.user = {
        OR: [
          { email: { contains: search, mode: 'insensitive' } },
          { name: { contains: search, mode: 'insensitive' } },
        ],
      };
    }

    const [subscriptions, total] = await Promise.all([
      prisma.userSubscription.findMany({
        where,
        include: { plan: true, user: { select: { id: true, email: true, name: true } } },
        skip: (page - 1) * limit,
        take: limit,
        orderBy: { createdAt: 'desc' },
      }),
      prisma.userSubscription.count({ where }),
    ]);

    return {
      subscriptions,
      pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
    };
  },

  /** Sync subscription from Stripe (e.g. after webhook) */
  async activateFromCheckout(
    userId: string,
    planId: number,
    stripeSubscriptionId: string,
    stripeCustomerId: string,
    currentPeriodEnd: Date,
  ) {
    // Deactivate any existing active sub
    await prisma.userSubscription.updateMany({
      where: { userId, status: 'active' },
      data: { status: 'cancelled', cancelledAt: new Date() },
    });

    return prisma.userSubscription.create({
      data: {
        userId,
        planId,
        status: 'active',
        startDate: new Date(),
        endDate: currentPeriodEnd,
        stripeSubscriptionId,
        stripeCustomerId,
        currentPeriodEnd,
      },
    });
  },

  /** Sync to Stripe: create/update product + prices for a plan */
  async syncPlanToStripe(planId: number) {
    const stripe = await getStripe();
    const plan = await prisma.subscriptionPlan.findUnique({ where: { id: planId } });
    if (!plan) throw new Error('Plan not found');

    let stripeProductId = plan.stripeProductId;

    // Create or update Stripe product
    if (stripeProductId) {
      await stripe.products.update(stripeProductId, {
        name: plan.name,
        description: plan.description || undefined,
        active: plan.isActive,
      });
    } else {
      const product = await stripe.products.create({
        name: plan.name,
        description: plan.description || undefined,
        active: plan.isActive,
        metadata: { planId: plan.id.toString() },
      });
      stripeProductId = product.id;
    }

    // Create monthly price if needed
    let stripePriceId = plan.stripePriceId;
    if (!stripePriceId && plan.priceCAD > 0) {
      const price = await stripe.prices.create({
        product: stripeProductId,
        unit_amount: plan.priceCAD,
        currency: 'cad',
        recurring: { interval: 'month' },
      });
      stripePriceId = price.id;
    }

    // Create yearly price if needed
    let stripeYearlyPriceId = plan.stripeYearlyPriceId;
    if (!stripeYearlyPriceId && plan.yearlyPriceCAD && plan.yearlyPriceCAD > 0) {
      const price = await stripe.prices.create({
        product: stripeProductId,
        unit_amount: plan.yearlyPriceCAD,
        currency: 'cad',
        recurring: { interval: 'year' },
      });
      stripeYearlyPriceId = price.id;
    }

    return prisma.subscriptionPlan.update({
      where: { id: planId },
      data: { stripeProductId, stripePriceId, stripeYearlyPriceId },
    });
  },

  /** Export subscriptions to CSV */
  async exportToCsv(filters: { search?: string; status?: SubscriptionStatus; planId?: number }) {
    const where: Prisma.UserSubscriptionWhereInput = {};
    if (filters.status) where.status = filters.status;
    if (filters.planId) where.planId = filters.planId;
    if (filters.search) {
      where.user = {
        OR: [
          { email: { contains: filters.search, mode: 'insensitive' } },
          { name: { contains: filters.search, mode: 'insensitive' } },
        ],
      };
    }

    const subscriptions = await prisma.userSubscription.findMany({
      where,
      include: { plan: true, user: { select: { id: true, email: true, name: true } } },
      orderBy: { createdAt: 'desc' },
    });

    return stringify(subscriptions.map((s) => ({
      'User Name': sanitizeCsvValue(s.user?.name || ''),
      'User Email': sanitizeCsvValue(s.user?.email || ''),
      'Plan': sanitizeCsvValue(s.plan?.name || ''),
      'Price (CAD/mo)': s.plan ? (s.plan.priceCAD / 100).toFixed(2) : '',
      'Status': s.status,
      'Started': s.startDate.toISOString(),
      'Current Period End': s.currentPeriodEnd ? s.currentPeriodEnd.toISOString() : '',
      'Cancel at Period End': s.cancelAtPeriodEnd ? 'Yes' : 'No',
      'Cancelled At': s.cancelledAt ? s.cancelledAt.toISOString() : '',
      'Subscription ID': s.id.toString(),
      'User ID': s.userId,
    })), { header: true });
  },

  /** List active plans for public display */
  async listActivePlans() {
    return prisma.subscriptionPlan.findMany({
      where: { isActive: true },
      orderBy: { displayOrder: 'asc' },
    });
  },

  /** Admin CRUD for plans */
  async createPlan(data: Prisma.SubscriptionPlanUncheckedCreateInput) {
    return prisma.subscriptionPlan.create({ data });
  },

  async updatePlan(id: number, data: Prisma.SubscriptionPlanUncheckedUpdateInput) {
    return prisma.subscriptionPlan.update({ where: { id }, data });
  },

  async deletePlan(id: number) {
    // Check for active subscriptions
    const activeSubs = await prisma.userSubscription.count({
      where: { planId: id, status: 'active' },
    });
    if (activeSubs > 0) {
      throw new Error(`Cannot delete plan with ${activeSubs} active subscriptions`);
    }
    return prisma.subscriptionPlan.delete({ where: { id } });
  },

  /** Dashboard stats */
  async getDashboardStats() {
    const [
      activeSubscribers,
      totalRevenue,
      plans,
      recentSubscriptions,
    ] = await Promise.all([
      prisma.userSubscription.count({ where: { status: 'active' } }),
      prisma.order.aggregate({
        where: { status: 'COMPLETED' },
        _sum: { amountCAD: true },
      }),
      prisma.subscriptionPlan.findMany({ where: { isActive: true } }),
      prisma.userSubscription.findMany({
        where: { status: 'active' },
        include: { plan: true },
        orderBy: { createdAt: 'desc' },
        take: 10,
      }),
    ]);

    // Calculate MRR from active subscriptions
    let mrr = 0;
    for (const sub of recentSubscriptions) {
      mrr += sub.plan.priceCAD;
    }

    // Count total active subs for accurate MRR
    const allActiveSubs = await prisma.userSubscription.findMany({
      where: { status: 'active' },
      include: { plan: true },
    });
    mrr = allActiveSubs.reduce((sum, sub) => sum + sub.plan.priceCAD, 0);

    return {
      activeSubscribers,
      totalRevenue: totalRevenue._sum.amountCAD || 0,
      mrr,
      planCount: plans.length,
    };
  },
};
