import { prisma } from '../../config/database';
import { getStripe } from '../../services/stripe.client';
import { env } from '../../config/env';
import { paymentSettingsService } from './payment-settings.service';
import { stringify } from 'csv-stringify/sync';
import { logger } from '../../utils/logger';

/** 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 donationsService = {
  /** Create a Stripe Checkout session for a donation */
  async createDonationCheckout(
    amountCents: number,
    email: string,
    name?: string,
    message?: string,
    isAnonymous?: boolean,
    donationPageId?: string,
    donationPageSlug?: string,
    donationPageTitle?: string,
    campaignId?: string,
  ) {
    const settings = await paymentSettingsService.get();
    if (!settings.enableDonations) throw new Error('Donations are currently disabled');

    // Use page-specific minimum if provided via page route, otherwise global
    if (!donationPageId && amountCents < settings.donationMinimum) {
      throw new Error(`Minimum donation is $${(settings.donationMinimum / 100).toFixed(2)}`);
    }

    const stripe = await getStripe();

    const productName = donationPageTitle ? `Donation — ${donationPageTitle}` : 'Donation';
    const cancelUrl = donationPageSlug
      ? `${env.ADMIN_URL}/donate/${donationPageSlug}`
      : `${env.ADMIN_URL}/donate`;

    const session = await stripe.checkout.sessions.create({
      mode: 'payment',
      line_items: [{
        price_data: {
          currency: settings.defaultCurrency || 'cad',
          product_data: {
            name: productName,
            description: donationPageTitle || settings.donationPageTitle || 'Support Our Work',
          },
          unit_amount: amountCents,
        },
        quantity: 1,
      }],
      customer_email: email,
      success_url: `${env.ADMIN_URL}/payments/success?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: cancelUrl,
      metadata: {
        type: 'donation',
        email,
        name: name || '',
        message: message || '',
        isAnonymous: isAnonymous ? 'true' : 'false',
        donationPageId: donationPageId || '',
        campaignId: campaignId || '',
      },
    });

    // Create pending order
    await prisma.order.create({
      data: {
        amountCAD: amountCents,
        status: 'PENDING',
        stripeCheckoutSessionId: session.id,
        type: 'donation',
        buyerEmail: email,
        buyerName: name || null,
        donorMessage: message || null,
        isAnonymous: isAnonymous || false,
        donationPageId: donationPageId || null,
        influenceCampaignId: campaignId || null,
      },
    });

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

  /** List donations (admin) */
  async listDonations(filters: { page: number; limit: number; search?: string; donationPageId?: string }) {
    const { page, limit, search, donationPageId } = filters;
    const where: Record<string, unknown> = { type: 'donation' };
    if (search) {
      (where as Record<string, unknown>).OR = [
        { buyerEmail: { contains: search, mode: 'insensitive' } },
        { buyerName: { contains: search, mode: 'insensitive' } },
      ];
    }
    if (donationPageId === 'general') {
      (where as Record<string, unknown>).donationPageId = null;
    } else if (donationPageId) {
      (where as Record<string, unknown>).donationPageId = donationPageId;
    }

    const [orders, total] = await Promise.all([
      prisma.order.findMany({
        where: where as import('@prisma/client').Prisma.OrderWhereInput,
        skip: (page - 1) * limit,
        take: limit,
        orderBy: { createdAt: 'desc' },
        include: { donationPage: { select: { id: true, title: true, slug: true } } },
      }),
      prisma.order.count({ where: where as import('@prisma/client').Prisma.OrderWhereInput }),
    ]);

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

  /** Refund a donation via Stripe */
  async refundDonation(orderId: string, reason?: string) {
    const order = await prisma.order.findUnique({ where: { id: orderId } });
    if (!order) throw new Error('Donation not found');
    if (order.type !== 'donation') throw new Error('Order is not a donation');
    if (order.status !== 'COMPLETED') throw new Error('Only completed donations can be refunded');
    if (!order.stripePaymentIntentId) throw new Error('No Stripe payment intent found for this donation');

    const stripe = await getStripe();

    await stripe.refunds.create({
      payment_intent: order.stripePaymentIntentId,
      reason: 'requested_by_customer',
      metadata: {
        admin_reason: reason || 'Admin-initiated refund',
        order_id: orderId,
      },
    });

    // Stripe refund succeeded — update DB. If this fails, the charge.refunded
    // webhook will reconcile the status as a fallback.
    let updated;
    try {
      updated = await prisma.order.update({
        where: { id: orderId },
        data: { status: 'REFUNDED' },
      });
    } catch (dbErr) {
      logger.error(`Stripe refund succeeded but DB update failed for order ${orderId}. Webhook will reconcile.`, dbErr);
      throw new Error('Refund processed by Stripe but local status update failed. It will be reconciled shortly.');
    }

    logger.info(`Donation refunded: ${orderId}, $${(order.amountCAD / 100).toFixed(2)}`, {
      orderId,
      reason: reason || 'No reason provided',
    });

    return updated;
  },

  /** Export donations to CSV */
  async exportToCsv(filters: { search?: string; status?: string; donationPageId?: string }) {
    const where: Record<string, unknown> = { type: 'donation' };
    if (filters.status) {
      (where as Record<string, unknown>).status = filters.status;
    }
    if (filters.search) {
      (where as Record<string, unknown>).OR = [
        { buyerEmail: { contains: filters.search, mode: 'insensitive' } },
        { buyerName: { contains: filters.search, mode: 'insensitive' } },
      ];
    }
    if (filters.donationPageId === 'general') {
      (where as Record<string, unknown>).donationPageId = null;
    } else if (filters.donationPageId) {
      (where as Record<string, unknown>).donationPageId = filters.donationPageId;
    }

    const orders = await prisma.order.findMany({
      where: where as import('@prisma/client').Prisma.OrderWhereInput,
      orderBy: { createdAt: 'desc' },
      include: { donationPage: { select: { title: true } } },
    });

    return stringify(orders.map((o) => ({
      'Date': o.createdAt.toISOString(),
      'Donor Name': sanitizeCsvValue(o.isAnonymous ? 'Anonymous' : (o.buyerName || '')),
      'Donor Email': sanitizeCsvValue(o.isAnonymous ? '' : (o.buyerEmail || '')),
      'Amount (CAD)': (o.amountCAD / 100).toFixed(2),
      'Status': o.status,
      'Donation Page': sanitizeCsvValue(o.donationPage?.title || 'General'),
      'Message': sanitizeCsvValue(o.donorMessage || ''),
      'Anonymous': o.isAnonymous ? 'Yes' : 'No',
      'Completed At': o.completedAt ? o.completedAt.toISOString() : '',
      'Order ID': o.id,
    })), { header: true });
  },

  /** Get donation stats */
  async getDonationStats() {
    const [totalDonations, totalAmount, recentDonations] = await Promise.all([
      prisma.order.count({ where: { type: 'donation', status: 'COMPLETED' } }),
      prisma.order.aggregate({
        where: { type: 'donation', status: 'COMPLETED' },
        _sum: { amountCAD: true },
      }),
      prisma.order.findMany({
        where: { type: 'donation', status: 'COMPLETED' },
        orderBy: { createdAt: 'desc' },
        take: 5,
      }),
    ]);

    return {
      totalDonations,
      totalAmount: totalAmount._sum.amountCAD || 0,
      recentDonations,
    };
  },
};
