/**
 * Admin Transaction Management Routes
 *
 * Endpoints for viewing and managing transactions, payments, and credit history
 */

import { Router, Request, Response } from 'express';
import { z } from 'zod';
import { PrismaClient } from '@prisma/client';
import { authenticateAdmin } from '../../middleware/auth';
import { logger } from '../../utils/logger';
import { successResponse, errorResponse } from '../../utils/errorHandler';
import { refundPayment } from '../../services/paypalService';
import { PaginatedResponse } from '../../types';

const router = Router();
const prisma = new PrismaClient();

/**
 * GET /v1/admin/transactions
 * List all transactions with pagination and filtering
 */
router.get('/', authenticateAdmin, async (req: Request, res: Response) => {
  try {
    const {
      page = '1',
      limit = '20',
      userId,
      type,
      startDate,
      endDate,
      sortBy = 'created_at',
      sortOrder = 'desc',
      format,
    } = req.query;

    const pageNum = Math.max(1, parseInt(page as string));
    const limitNum = Math.min(100, Math.max(1, parseInt(limit as string)));
    const skip = (pageNum - 1) * limitNum;

    // Build where clause
    const where: any = {};

    // Filter by user
    if (userId) {
      where.user_id = userId as string;
    }

    // Filter by type
    if (type) {
      const types = (type as string).split(',');
      where.type = types.length === 1 ? types[0] : { in: types };
    }

    // Filter by date range
    if (startDate || endDate) {
      where.created_at = {};
      if (startDate) {
        where.created_at.gte = new Date(startDate as string);
      }
      if (endDate) {
        where.created_at.lte = new Date(endDate as string);
      }
    }

    // Build order clause
    const validSortFields = ['created_at', 'amount', 'balance_after'];
    const sortField = validSortFields.includes(sortBy as string) ? sortBy : 'created_at';
    const orderBy = {
      [sortField as string]: sortOrder === 'asc' ? 'asc' : 'desc',
    };

    // Execute queries
    const [transactions, total] = await Promise.all([
      prisma.creditTransaction.findMany({
        where,
        skip,
        take: limitNum,
        orderBy,
        include: {
          user: {
            select: {
              id: true,
              email: true,
              status: true,
            },
          },
          job: {
            select: {
              id: true,
              status: true,
              source_lang: true,
              target_lang: true,
              model: true,
            },
          },
          payment: {
            select: {
              id: true,
              amount: true,
              status: true,
              type: true,
            },
          },
        },
      }),
      prisma.creditTransaction.count({ where }),
    ]);

    // If CSV format is requested
    if (format === 'csv') {
      const csvData = convertTransactionsToCSV(transactions);
      res.setHeader('Content-Type', 'text/csv');
      res.setHeader('Content-Disposition', `attachment; filename="transactions-${new Date().toISOString().split('T')[0]}.csv"`);
      res.send(csvData);
      return;
    }

    // Calculate pagination metadata
    const totalPages = Math.ceil(total / limitNum);

    const response: PaginatedResponse<any> = {
      data: transactions.map((tx) => ({
        id: tx.id,
        userId: tx.user_id,
        user: {
          email: tx.user.email,
          status: tx.user.status,
        },
        type: tx.type,
        amount: tx.amount,
        balanceAfter: tx.balance_after,
        description: tx.description,
        relatedJob: tx.job
          ? {
              id: tx.job.id,
              status: tx.job.status,
              sourceLang: tx.job.source_lang,
              targetLang: tx.job.target_lang,
              model: tx.job.model,
            }
          : null,
        relatedPayment: tx.payment
          ? {
              id: tx.payment.id,
              amount: tx.payment.amount.toString(),
              status: tx.payment.status,
              type: tx.payment.type,
            }
          : null,
        createdAt: tx.created_at.toISOString(),
      })),
      pagination: {
        page: pageNum,
        limit: limitNum,
        total,
        totalPages,
        hasNext: pageNum < totalPages,
        hasPrev: pageNum > 1,
      },
    };

    res.json(response);
  } catch (error) {
    logger.error('Error fetching transactions list', { error, adminId: req.admin?.id });
    res.status(500).json({
      error: true,
      code: 'INTERNAL_ERROR',
      message: 'Failed to fetch transactions',
      timestamp: new Date().toISOString(),
    });
  }
});

/**
 * GET /v1/admin/transactions/:id
 * Get detailed information about a specific transaction
 */
router.get('/:id', authenticateAdmin, async (req: Request, res: Response) => {
  try {
    const { id } = req.params;

    const transaction = await prisma.creditTransaction.findUnique({
      where: { id },
      include: {
        user: {
          select: {
            id: true,
            email: true,
            status: true,
            subscriptions: {
              select: {
                plan_tier: true,
                status: true,
              },
            },
          },
        },
        job: {
          select: {
            id: true,
            status: true,
            source_lang: true,
            target_lang: true,
            model: true,
            tone: true,
            tokens_used: true,
            input_tokens: true,
            output_tokens: true,
            cost: true,
            created_at: true,
            completed_at: true,
          },
        },
        payment: {
          select: {
            id: true,
            amount: true,
            currency: true,
            status: true,
            type: true,
            paypal_payment_id: true,
            created_at: true,
          },
        },
      },
    });

    if (!transaction) {
      res.status(404).json({
        error: true,
        code: 'TRANSACTION_NOT_FOUND',
        message: 'Transaction not found',
        timestamp: new Date().toISOString(),
      });
      return;
    }

    res.json({
      id: transaction.id,
      userId: transaction.user_id,
      user: {
        id: transaction.user.id,
        email: transaction.user.email,
        status: transaction.user.status,
        subscription: transaction.user.subscriptions[0]
          ? {
              plan: transaction.user.subscriptions[0].plan_tier,
              status: transaction.user.subscriptions[0].status,
            }
          : null,
      },
      type: transaction.type,
      amount: transaction.amount,
      balanceAfter: transaction.balance_after,
      description: transaction.description,
      relatedJob: transaction.job
        ? {
            id: transaction.job.id,
            status: transaction.job.status,
            sourceLang: transaction.job.source_lang,
            targetLang: transaction.job.target_lang,
            model: transaction.job.model,
            tone: transaction.job.tone,
            tokensUsed: transaction.job.tokens_used,
            inputTokens: transaction.job.input_tokens,
            outputTokens: transaction.job.output_tokens,
            cost: transaction.job.cost.toString(),
            createdAt: transaction.job.created_at.toISOString(),
            completedAt: transaction.job.completed_at?.toISOString() || null,
          }
        : null,
      relatedPayment: transaction.payment
        ? {
            id: transaction.payment.id,
            amount: transaction.payment.amount.toString(),
            currency: transaction.payment.currency,
            status: transaction.payment.status,
            type: transaction.payment.type,
            paypalPaymentId: transaction.payment.paypal_payment_id,
            createdAt: transaction.payment.created_at.toISOString(),
          }
        : null,
      createdAt: transaction.created_at.toISOString(),
    });
  } catch (error) {
    logger.error('Error fetching transaction details', {
      error,
      transactionId: req.params.id,
      adminId: req.admin?.id,
    });
    res.status(500).json({
      error: true,
      code: 'INTERNAL_ERROR',
      message: 'Failed to fetch transaction details',
      timestamp: new Date().toISOString(),
    });
  }
});

/**
 * GET /v1/admin/transactions/payments
 * List all payments with pagination and filtering
 */
router.get('/payments', authenticateAdmin, async (req: Request, res: Response) => {
  try {
    const {
      page = '1',
      limit = '20',
      userId,
      status,
      type,
      startDate,
      endDate,
      sortBy = 'created_at',
      sortOrder = 'desc',
      format,
    } = req.query;

    const pageNum = Math.max(1, parseInt(page as string));
    const limitNum = Math.min(100, Math.max(1, parseInt(limit as string)));
    const skip = (pageNum - 1) * limitNum;

    // Build where clause
    const where: any = {};

    // Filter by user
    if (userId) {
      where.user_id = userId as string;
    }

    // Filter by status
    if (status) {
      const statuses = (status as string).split(',');
      where.status = statuses.length === 1 ? statuses[0] : { in: statuses };
    }

    // Filter by type
    if (type) {
      const types = (type as string).split(',');
      where.type = types.length === 1 ? types[0] : { in: types };
    }

    // Filter by date range
    if (startDate || endDate) {
      where.created_at = {};
      if (startDate) {
        where.created_at.gte = new Date(startDate as string);
      }
      if (endDate) {
        where.created_at.lte = new Date(endDate as string);
      }
    }

    // Build order clause
    const validSortFields = ['created_at', 'amount', 'status'];
    const sortField = validSortFields.includes(sortBy as string) ? sortBy : 'created_at';
    const orderBy = {
      [sortField as string]: sortOrder === 'asc' ? 'asc' : 'desc',
    };

    // Execute queries
    const [payments, total] = await Promise.all([
      prisma.payment.findMany({
        where,
        skip,
        take: limitNum,
        orderBy,
        include: {
          user: {
            select: {
              id: true,
              email: true,
              status: true,
            },
          },
          subscription: {
            select: {
              id: true,
              plan_tier: true,
              status: true,
            },
          },
        },
      }),
      prisma.payment.count({ where }),
    ]);

    // If CSV format is requested
    if (format === 'csv') {
      const csvData = convertPaymentsToCSV(payments);
      res.setHeader('Content-Type', 'text/csv');
      res.setHeader('Content-Disposition', `attachment; filename="payments-${new Date().toISOString().split('T')[0]}.csv"`);
      res.send(csvData);
      return;
    }

    // Calculate pagination metadata
    const totalPages = Math.ceil(total / limitNum);

    const response: PaginatedResponse<any> = {
      data: payments.map((payment) => ({
        id: payment.id,
        userId: payment.user_id,
        user: {
          email: payment.user.email,
          status: payment.user.status,
        },
        paypalPaymentId: payment.paypal_payment_id,
        amount: payment.amount.toString(),
        currency: payment.currency,
        status: payment.status,
        type: payment.type,
        subscription: payment.subscription
          ? {
              id: payment.subscription.id,
              plan: payment.subscription.plan_tier,
              status: payment.subscription.status,
            }
          : null,
        createdAt: payment.created_at.toISOString(),
      })),
      pagination: {
        page: pageNum,
        limit: limitNum,
        total,
        totalPages,
        hasNext: pageNum < totalPages,
        hasPrev: pageNum > 1,
      },
    };

    res.json(response);
  } catch (error) {
    logger.error('Error fetching payments list', { error, adminId: req.admin?.id });
    res.status(500).json({
      error: true,
      code: 'INTERNAL_ERROR',
      message: 'Failed to fetch payments',
      timestamp: new Date().toISOString(),
    });
  }
});

/**
 * Zod schema for refund request body
 */
const refundPaymentSchema = z.object({
  amount: z.number().positive().optional(),
  reason: z.string().max(500).optional(),
});

/**
 * POST /v1/admin/transactions/payments/:id/refund
 * Issue a PayPal refund for a completed payment.
 *
 * Steps:
 *  1. Look up the Payment record by internal ID.
 *  2. Reject if status is not 'completed' (already refunded, failed, pending).
 *  3. Reject if paypal_payment_id is null (legacy mock/manual payment).
 *  4. Call PayPal v1 sale-refund API.
 *  5. Mark payment status as 'refunded' optimistically — the PAYMENT.SALE.REFUNDED
 *     webhook will also update it, but closing the loop here gives the admin UI
 *     immediate feedback without waiting for the async webhook delivery.
 *  6. DO NOT reverse credits here. The PAYMENT.SALE.REFUNDED webhook handler in
 *     webhooks.ts (handlePaymentRefunded) already deducts credits when PayPal
 *     confirms the refund. Reversing credits here as well would cause a
 *     double-deduction and leave the user with a negative balance.
 *  7. Write an AuditLog entry recording the admin action.
 */
router.post('/payments/:id/refund', authenticateAdmin, async (req: Request, res: Response) => {
  try {
    const { id } = req.params;

    // Validate body
    const parseResult = refundPaymentSchema.safeParse(req.body);
    if (!parseResult.success) {
      res.status(400).json(errorResponse('VALIDATION_ERROR', parseResult.error.message));
      return;
    }
    const { amount, reason } = parseResult.data;

    // 1. Look up payment
    const payment = await prisma.payment.findUnique({ where: { id } });

    if (!payment) {
      res.status(404).json(errorResponse('PAYMENT_NOT_FOUND', 'Payment not found'));
      return;
    }

    // 2. Guard against invalid statuses
    if (payment.status !== 'completed') {
      res.status(409).json(
        errorResponse(
          'PAYMENT_NOT_REFUNDABLE',
          `Cannot refund a payment with status '${payment.status}'. Only 'completed' payments can be refunded.`
        )
      );
      return;
    }

    // 3. Guard against non-PayPal payments
    if (!payment.paypal_payment_id) {
      res.status(400).json(
        errorResponse('NON_PAYPAL_PAYMENT', 'Cannot refund non-PayPal payment: paypal_payment_id is not set')
      );
      return;
    }

    // 4. Call PayPal refund API
    let refundResult: { refund_id: string };
    try {
      refundResult = await refundPayment({
        paypalPaymentId: payment.paypal_payment_id,
        amount,
        currency: payment.currency,
        reason,
      });
    } catch (paypalError: any) {
      const message =
        paypalError?.message ??
        (typeof paypalError === 'object' ? JSON.stringify(paypalError) : String(paypalError));

      logger.error('PayPal refund API call failed in admin endpoint', {
        paymentId: id,
        paypalPaymentId: payment.paypal_payment_id,
        error: message,
        adminId: req.admin?.id,
      });

      res.status(502).json(errorResponse('PAYPAL_REFUND_FAILED', `PayPal refund failed: ${message}`));
      return;
    }

    // 5. Mark payment as refunded in our DB.
    //    Note: credits are NOT reversed here — see step 6 comment above.
    await prisma.payment.update({
      where: { id },
      data: { status: 'refunded' },
    });

    // 7. Write audit log.
    //    AuditLog.user_id FK points to the `users` table (not admin_users).
    //    We store the admin identity inside `details` to preserve the relation
    //    with the affected user, and avoid an FK violation since req.admin.id
    //    is an admin_users UUID — not a users UUID.
    await prisma.auditLog.create({
      data: {
        action: 'payment.refund',
        resource_type: 'payment',
        resource_id: id,
        ip_address: (req as any).clientIp,
        user_agent: req.headers['user-agent'],
        details: {
          adminId: req.admin?.id,
          adminEmail: req.admin?.email,
          paypalPaymentId: payment.paypal_payment_id,
          paypalRefundId: refundResult.refund_id,
          amount: amount ?? null,
          currency: payment.currency,
          reason: reason ?? null,
        },
      },
    });

    logger.info('Admin payment refund processed', {
      paymentId: id,
      paypalPaymentId: payment.paypal_payment_id,
      paypalRefundId: refundResult.refund_id,
      amount,
      adminId: req.admin?.id,
    });

    res.json(
      successResponse({
        paymentId: id,
        paypalRefundId: refundResult.refund_id,
        status: 'refunded',
        amount: amount ?? null,
        currency: payment.currency,
      })
    );
  } catch (error) {
    logger.error('Error processing payment refund', {
      error,
      paymentId: req.params.id,
      adminId: req.admin?.id,
    });
    res.status(500).json(errorResponse('INTERNAL_ERROR', 'Failed to process refund'));
  }
});

/**
 * Helper function to convert transactions to CSV format
 */
function convertTransactionsToCSV(transactions: any[]): string {
  const headers = [
    'Transaction ID',
    'User Email',
    'Type',
    'Amount',
    'Balance After',
    'Description',
    'Related Job ID',
    'Related Payment ID',
    'Created At',
  ];

  const rows = transactions.map((tx) => [
    tx.id,
    tx.user.email,
    tx.type,
    tx.amount.toString(),
    tx.balance_after.toString(),
    `"${tx.description.replace(/"/g, '""')}"`, // Escape quotes in description
    tx.related_job_id || '',
    tx.related_payment_id || '',
    tx.created_at.toISOString(),
  ]);

  const csvLines = [headers.join(','), ...rows.map((row) => row.join(','))];

  return csvLines.join('\n');
}

/**
 * Helper function to convert payments to CSV format
 */
function convertPaymentsToCSV(payments: any[]): string {
  const headers = [
    'Payment ID',
    'User Email',
    'PayPal Payment ID',
    'Amount',
    'Currency',
    'Status',
    'Type',
    'Subscription Plan',
    'Created At',
  ];

  const rows = payments.map((payment) => [
    payment.id,
    payment.user.email,
    payment.paypal_payment_id || '',
    payment.amount.toString(),
    payment.currency,
    payment.status,
    payment.type,
    payment.subscription?.plan_tier || '',
    payment.created_at.toISOString(),
  ]);

  const csvLines = [headers.join(','), ...rows.map((row) => row.join(','))];

  return csvLines.join('\n');
}

export default router;
