// Bank Reconciliation System
// Matches Stripe payouts to AR records, flags discrepancies

import prisma from '@/lib/db';
import { logSimpleAction } from './agent-logger';
import { BRAND } from '@/lib/white-label/constants';

export interface ReconciliationResult {
  reconciliationId: string;
  status: 'matched' | 'discrepancy' | 'partial';
  expectedAmount: number;
  actualAmount: number;
  discrepancy: number;
  matchedRecords: number;
  unmatchedItems: string[];
}

export interface PayoutData {
  stripePayoutId: string;
  amount: number;
  currency: string;
  arrivalDate: Date;
  paymentIntentIds: string[];
}

// Mock Stripe payout data for testing (when Stripe keys not configured)
function getMockPayouts(): PayoutData[] {
  const now = new Date();
  return [
    {
      stripePayoutId: 'po_mock_001',
      amount: 299.99,
      currency: 'USD',
      arrivalDate: new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000),
      paymentIntentIds: ['pi_mock_001', 'pi_mock_002'],
    },
    {
      stripePayoutId: 'po_mock_002',
      amount: 149.99,
      currency: 'USD',
      arrivalDate: new Date(now.getTime() - 1 * 24 * 60 * 60 * 1000),
      paymentIntentIds: ['pi_mock_003'],
    },
  ];
}

async function fetchStripePayouts(startDate: Date, endDate: Date): Promise<PayoutData[]> {
  const stripeSecretKey = process.env.STRIPE_SECRET_KEY;
  
  if (!stripeSecretKey || stripeSecretKey.startsWith('sk_placeholder')) {
    console.log('[Reconciliation] Using mock Stripe data');
    return getMockPayouts();
  }
  
  try {
    // Fetch payouts from Stripe API
    const response = await fetch(
      `https://api.stripe.com/v1/payouts?created[gte]=${Math.floor(startDate.getTime() / 1000)}&created[lte]=${Math.floor(endDate.getTime() / 1000)}&limit=100`,
      {
        headers: {
          'Authorization': `Bearer ${stripeSecretKey}`,
          'Content-Type': 'application/x-www-form-urlencoded',
        },
      }
    );
    
    if (!response.ok) {
      console.error('[Reconciliation] Stripe API error:', await response.text());
      return getMockPayouts();
    }
    
    const data = await response.json();
    
    // For each payout, get the associated balance transactions
    const payouts: PayoutData[] = [];
    for (const payout of data.data || []) {
      // Fetch balance transactions for this payout
      const btResponse = await fetch(
        `https://api.stripe.com/v1/balance_transactions?payout=${payout.id}&limit=100`,
        {
          headers: {
            'Authorization': `Bearer ${stripeSecretKey}`,
          },
        }
      );
      
      const btData = btResponse.ok ? await btResponse.json() : { data: [] };
      const paymentIntentIds = btData.data
        .filter((bt: { source: string }) => bt.source?.startsWith('ch_') || bt.source?.startsWith('pi_'))
        .map((bt: { source: string }) => bt.source);
      
      payouts.push({
        stripePayoutId: payout.id,
        amount: payout.amount / 100, // Convert from cents
        currency: payout.currency.toUpperCase(),
        arrivalDate: new Date(payout.arrival_date * 1000),
        paymentIntentIds,
      });
    }
    
    return payouts;
  } catch (error) {
    console.error('[Reconciliation] Error fetching Stripe payouts:', error);
    return getMockPayouts();
  }
}

export async function reconcilePayout(payout: PayoutData): Promise<ReconciliationResult> {
  // Find AR records that match the payment intent IDs
  const matchedArRecords = await prisma.accountsReceivable.findMany({
    where: {
      status: 'paid',
      metadata: {
        path: ['stripePaymentIntentId'],
        array_contains: payout.paymentIntentIds,
      },
    },
  });
  
  // If no matches by payment intent, try matching by amount and date range
  let arRecords = matchedArRecords;
  if (arRecords.length === 0) {
    const dayBefore = new Date(payout.arrivalDate.getTime() - 3 * 24 * 60 * 60 * 1000);
    arRecords = await prisma.accountsReceivable.findMany({
      where: {
        status: 'paid',
        paidDate: {
          gte: dayBefore,
          lte: payout.arrivalDate,
        },
      },
    });
  }
  
  const expectedAmount = arRecords.reduce((sum: number, ar: { amountPaid: number }) => sum + ar.amountPaid, 0);
  const discrepancy = Math.abs(payout.amount - expectedAmount);
  const unmatchedItems: string[] = [];
  
  // Check for unmatched payment intents
  const matchedPaymentIntents = arRecords
    .map((ar: any) => (ar.metadata as Record<string, unknown>)?.stripePaymentIntentId)
    .filter(Boolean);
  
  for (const pi of payout.paymentIntentIds) {
    if (!matchedPaymentIntents.includes(pi)) {
      unmatchedItems.push(`Unmatched payment: ${pi}`);
    }
  }
  
  let status: 'matched' | 'discrepancy' | 'partial' = 'matched';
  if (discrepancy > 0.01) { // More than 1 cent difference
    status = discrepancy > payout.amount * 0.05 ? 'discrepancy' : 'partial';
  }
  
  // Create or update reconciliation record
  const existingRecon = await prisma.bankReconciliation.findFirst({
    where: { stripePayoutId: payout.stripePayoutId },
  });
  
  const reconData = {
    reconciliationId: `RECON-${payout.stripePayoutId || Date.now()}`,
    stripePayoutId: payout.stripePayoutId,
    periodStart: new Date(payout.arrivalDate.getTime() - 7 * 24 * 60 * 60 * 1000),
    periodEnd: payout.arrivalDate,
    expectedAmount,
    actualAmount: payout.amount,
    discrepancy,
    status,
    matchedArIds: arRecords.map((ar: any) => ar.id),
    unmatchedItems: [...unmatchedItems],
  };
  
  let reconciliation;
  if (existingRecon) {
    reconciliation = await prisma.bankReconciliation.update({
      where: { id: existingRecon.id },
      data: reconData,
    });
  } else {
    reconciliation = await prisma.bankReconciliation.create({
      data: reconData,
    });
  }
  
  // Log agent action
  await logSimpleAction({
    actionType: 'bank_reconciliation',
    targetEntity: 'BankReconciliation',
    targetId: reconciliation.id,
    input: { payout },
    output: { status, discrepancy, matchedRecords: arRecords.length },
    confidence: status === 'matched' ? 1.0 : 0.7,
    anomalyDetected: status === 'discrepancy',
    anomalyType: status === 'discrepancy' ? 'reconciliation_discrepancy' : undefined,
  });
  
  // Send alert if significant discrepancy
  if (status === 'discrepancy' && discrepancy > 100) {
    await sendReconciliationAlert(reconciliation.id, payout, discrepancy);
  }
  
  return {
    reconciliationId: reconciliation.reconciliationId,
    status,
    expectedAmount,
    actualAmount: payout.amount,
    discrepancy,
    matchedRecords: arRecords.length,
    unmatchedItems,
  };
}

async function sendReconciliationAlert(
  reconciliationId: string,
  payout: PayoutData,
  discrepancy: number
): Promise<void> {
  try {
    const htmlContent = `
      <div style="font-family: 'Inter', sans-serif; background: #0C1222; color: #e2e8f0; padding: 30px; border-radius: 12px;">
        <h2 style="color: #ef4444;">⚠️ Reconciliation Discrepancy Detected</h2>
        <p>A significant discrepancy was found during bank reconciliation:</p>
        <div style="background: rgba(239, 68, 68, 0.1); border: 1px solid #ef4444; border-radius: 8px; padding: 20px; margin: 20px 0;">
          <p><strong>Payout ID:</strong> ${payout.stripePayoutId}</p>
          <p><strong>Expected Amount:</strong> $${(payout.amount - discrepancy).toFixed(2)}</p>
          <p><strong>Actual Amount:</strong> $${payout.amount.toFixed(2)}</p>
          <p><strong>Discrepancy:</strong> <span style="color: #ef4444;">$${discrepancy.toFixed(2)}</span></p>
          <p><strong>Arrival Date:</strong> ${payout.arrivalDate.toLocaleDateString()}</p>
        </div>
        <p>Please review in the <a href="https://${BRAND.domain}/ghost-control/order-to-cash" style="color: #0EA5E9;">Order-to-Cash Dashboard</a></p>
      </div>
    `;
    
    await fetch('https://apps.abacus.ai/api/sendNotificationEmail', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        api_key: process.env.ABACUSAI_API_KEY,
        app_id: process.env.WEB_APP_ID,
        notification_id: process.env.NOTIF_ID_RECONCILIATION_ALERT,
        recipient_email: process.env.ADMIN_EMAIL || 'ghostfoundrysyndicate@outlook.com',
        subject: `⚠️ Reconciliation Alert: $${discrepancy.toFixed(2)} Discrepancy`,
        body_html: htmlContent,
      }),
    });
  } catch (error) {
    console.error('[Reconciliation] Failed to send alert:', error);
  }
}

export async function runReconciliation(
  startDate?: Date,
  endDate?: Date
): Promise<ReconciliationResult[]> {
  const end = endDate || new Date();
  const start = startDate || new Date(end.getTime() - 7 * 24 * 60 * 60 * 1000);
  
  const payouts = await fetchStripePayouts(start, end);
  const results: ReconciliationResult[] = [];
  
  for (const payout of payouts) {
    // Check if already recorded
    const existingPayout = await prisma.stripePayoutRecord.findUnique({
      where: { stripePayoutId: payout.stripePayoutId },
    });
    
    if (!existingPayout) {
      await prisma.stripePayoutRecord.create({
        data: {
          stripePayoutId: payout.stripePayoutId,
          amount: payout.amount,
          currency: payout.currency,
          status: 'paid',
          arrivalDate: payout.arrivalDate,
          paymentIntentIds: [...payout.paymentIntentIds],
        },
      });
    }
    
    const result = await reconcilePayout(payout);
    results.push(result);
  }
  
  return results;
}

export async function getReconciliationStats(): Promise<{
  totalReconciliations: number;
  matched: number;
  discrepancies: number;
  totalDiscrepancyAmount: number;
  pendingReview: number;
}> {
  const reconciliations = await prisma.bankReconciliation.findMany();
  
  return {
    totalReconciliations: reconciliations.length,
    matched: reconciliations.filter((r: any) => r.status === 'matched').length,
    discrepancies: reconciliations.filter((r: any) => r.status === 'discrepancy').length,
    totalDiscrepancyAmount: reconciliations
      .filter((r: any) => r.status === 'discrepancy')
      .reduce((sum: number, r: any) => sum + r.discrepancy, 0),
    pendingReview: reconciliations.filter((r: any) => r.status === 'pending').length,
  };
}

export async function resolveDiscrepancy(
  reconciliationId: string,
  resolution: string,
  resolvedBy: string
): Promise<void> {
  await prisma.bankReconciliation.update({
    where: { reconciliationId },
    data: {
      status: 'resolved',
      notes: resolution,
      resolvedBy,
      resolvedAt: new Date(),
    },
  });
  
  await logSimpleAction({
    actionType: 'discrepancy_resolution',
    targetEntity: 'BankReconciliation',
    targetId: reconciliationId,
    input: { resolution, resolvedBy },
    output: { status: 'resolved' },
    confidence: 1.0,
    anomalyDetected: false,
  });
}

export async function getRecentReconciliations(limit: number = 20): Promise<{
  id: string;
  reconciliationId: string;
  stripePayoutId: string | null;
  expectedAmount: number;
  actualAmount: number;
  discrepancy: number;
  status: string;
  createdAt: Date;
}[]> {
  return prisma.bankReconciliation.findMany({
    orderBy: { createdAt: 'desc' },
    take: limit,
  });
}
