/**
 * Order-to-Cash Orchestrator
 * Main autonomous workflow engine
 */

import prisma from '@/lib/db';
import { OrderToCashStage, AgentContext, StageResult } from './types';
import { logAgentAction } from './agent-logger';
import { detectOrderAnomalies, getMostSevereAnomaly, shouldRequireHumanReview } from './anomaly-detector';
import { createSniffCheckFromAnomaly } from './sniff-check';
import { recordOrderPatterns } from './pattern-engine';

const STAGE_ORDER: OrderToCashStage[] = [
  'order_placed',
  'payment_processing',
  'payment_confirmed',
  'invoice_generated',
  'transaction_recorded',
  'ar_updated',
  'fulfilled',
  'cash_reconciled',
  'completed',
];

export async function initializeOrderExecution(orderId: string, orderNumber: string): Promise<string> {
  const execution = await prisma.orderToCashExecution.create({
    data: {
      orderId,
      orderNumber,
      currentStage: 'order_placed',
      stageHistory: [{
        stage: 'order_placed',
        timestamp: new Date().toISOString(),
        agent: 'OrderOrchestrator',
        autonomous: true,
      }],
    },
  });

  return execution.id;
}

export async function advanceStage(
  executionId: string,
  result: StageResult
): Promise<void> {
  const execution = await prisma.orderToCashExecution.findUnique({
    where: { id: executionId },
  });

  if (!execution) throw new Error(`Execution ${executionId} not found`);

  const history = (execution.stageHistory as unknown[]) || [];
  history.push({
    stage: result.nextStage || result.stage,
    timestamp: new Date().toISOString(),
    success: result.success,
    data: result.data,
    anomaly: result.anomaly,
    requiresHumanReview: result.requiresHumanReview,
    error: result.error,
  });

  const updates: Record<string, unknown> = {
    stageHistory: history,
  };

  if (result.success && result.nextStage) {
    updates.currentStage = result.nextStage;
  }

  if (result.anomaly?.detected) {
    updates.anomaliesDetected = { increment: 1 };
  }

  if (result.nextStage === 'completed') {
    updates.status = 'completed';
    updates.completedAt = new Date();
    const startTime = execution.startedAt.getTime();
    updates.totalTimeMs = Date.now() - startTime;
  }

  if (result.error) {
    updates.status = 'failed';
    updates.error = result.error;
  }

  if (result.requiresHumanReview) {
    updates.status = 'paused';
  }

  await prisma.orderToCashExecution.update({
    where: { id: executionId },
    data: updates,
  });
}

export async function processPaymentConfirmed(
  orderId: string,
  stripePaymentIntentId: string,
  stripeCustomerId?: string
): Promise<void> {
  const order = await prisma.swagOrder.findUnique({
    where: { id: orderId },
    include: { items: true },
  });

  if (!order) throw new Error(`Order ${orderId} not found`);

  const context: AgentContext = {
    executionId: order.workflowExecutionId || orderId,
    orderId,
    orderNumber: order.orderNumber,
    agentName: 'PaymentAgent',
    timestamp: new Date(),
  };

  const startTime = Date.now();

  // 1. Update order with payment info
  await prisma.swagOrder.update({
    where: { id: orderId },
    data: {
      stripePaymentIntentId,
      stripeCustomerId,
      paymentStatus: 'paid',
      status: 'confirmed',
    },
  });

  await logAgentAction({
    context,
    actionType: 'confirm_payment',
    targetEntity: 'order',
    targetId: orderId,
    inputData: { stripePaymentIntentId, stripeCustomerId },
    outputData: { paymentStatus: 'paid', status: 'confirmed' },
    executionTimeMs: Date.now() - startTime,
  });

  // 2. Detect anomalies
  const anomalies = await detectOrderAnomalies({
    id: order.id,
    orderNumber: order.orderNumber,
    customerEmail: order.customerEmail,
    customerName: order.customerName,
    total: order.total,
    subtotal: order.subtotal,
    tax: order.tax,
    shipping: order.shipping,
    shippingAddress: order.shippingAddress as Record<string, unknown>,
    items: order.items.map((i: any) => ({
      quantity: i.quantity,
      unitPrice: i.unitPrice,
      totalPrice: i.totalPrice,
    })),
  });

  const mostSevere = getMostSevereAnomaly(anomalies);
  const requiresReview = shouldRequireHumanReview(anomalies);

  if (mostSevere) {
    await logAgentAction({
      context: { ...context, agentName: 'AnomalyDetector' },
      actionType: 'detect_anomaly',
      targetEntity: 'order',
      targetId: orderId,
      outputData: { anomalies },
      anomaly: mostSevere,
    });
  }

  if (requiresReview) {
    await createSniffCheckFromAnomaly(
      mostSevere!,
      'order',
      orderId,
      { orderNumber: order.orderNumber, total: order.total, customerEmail: order.customerEmail },
      context.executionId
    );

    // Pause execution until human reviews
    if (order.workflowExecutionId) {
      await advanceStage(order.workflowExecutionId, {
        success: false,
        stage: 'payment_confirmed',
        requiresHumanReview: true,
        anomaly: mostSevere!,
      });
    }
    return;
  }

  // 3. Generate invoice
  await generateInvoice(orderId, context);

  // 4. Create transaction
  await createTransaction(orderId, context);

  // 5. Update AR
  await updateAccountsReceivable(orderId, context);

  // 6. Record patterns for learning
  await recordOrderPatterns({
    orderId: order.id,
    customerEmail: order.customerEmail,
    shippingAddress: order.shippingAddress as Record<string, unknown>,
    subtotal: order.subtotal,
    tax: order.tax,
    shipping: order.shipping,
    total: order.total,
    paymentMethod: order.paymentMethod || 'card',
    items: order.items.map((i: any) => ({
      productId: i.productId,
      quantity: i.quantity,
      unitPrice: i.unitPrice,
    })),
  });

  // 7. Advance execution
  if (order.workflowExecutionId) {
    await advanceStage(order.workflowExecutionId, {
      success: true,
      stage: 'payment_confirmed',
      nextStage: 'ar_updated',
      data: { invoiceGenerated: true, transactionCreated: true, arUpdated: true },
    });
  }
}

async function generateInvoice(orderId: string, context: AgentContext): Promise<string> {
  const order = await prisma.swagOrder.findUnique({
    where: { id: orderId },
    include: { items: { include: { product: true } } },
  });

  if (!order) throw new Error(`Order ${orderId} not found`);

  const startTime = Date.now();
  const invoiceNumber = `INV-${order.orderNumber}`;
  const dueDate = new Date();
  dueDate.setDate(dueDate.getDate() + 30); // Net 30

  const invoiceItems = order.items.map((item: any) => ({
    productId: item.productId,
    productName: item.product.name,
    quantity: item.quantity,
    unitPrice: item.unitPrice,
    totalPrice: item.totalPrice,
  }));

  const invoice = await prisma.invoice.create({
    data: {
      invoiceNumber,
      type: 'customer',
      customerId: order.customerEmail, // Using email as customer ID for now
      issueDate: new Date(),
      dueDate,
      status: 'paid', // Already paid via Stripe
      subtotal: order.subtotal,
      tax: order.tax,
      total: order.total,
      amountPaid: order.total,
      items: invoiceItems,
      metadata: {
        orderId: order.id,
        orderNumber: order.orderNumber,
        customerEmail: order.customerEmail,
        customerName: order.customerName,
      },
    },
  });

  // Link invoice to order
  await prisma.swagOrder.update({
    where: { id: orderId },
    data: { invoiceId: invoice.id },
  });

  await logAgentAction({
    context: { ...context, agentName: 'InvoiceAgent' },
    actionType: 'generate_invoice',
    targetEntity: 'invoice',
    targetId: invoice.id,
    inputData: { orderId, orderNumber: order.orderNumber },
    outputData: { invoiceId: invoice.id, invoiceNumber, total: order.total },
    executionTimeMs: Date.now() - startTime,
  });

  return invoice.id;
}

async function createTransaction(orderId: string, context: AgentContext): Promise<string> {
  const order = await prisma.swagOrder.findUnique({ where: { id: orderId } });
  if (!order) throw new Error(`Order ${orderId} not found`);

  const startTime = Date.now();

  // Find or create revenue account
  let revenueAccount = await prisma.account.findFirst({
    where: { code: 'REV-001', type: 'revenue' },
  });

  if (!revenueAccount) {
    revenueAccount = await prisma.account.create({
      data: {
        code: 'REV-001',
        name: 'Swag Store Revenue',
        type: 'revenue',
        category: 'income',
        balance: 0,
      },
    });
  }

  const transaction = await prisma.transaction.create({
    data: {
      type: 'income',
      accountId: revenueAccount.id,
      referenceNo: order.orderNumber,
      description: `Swag Store Order ${order.orderNumber}`,
      amount: order.total,
      date: new Date(),
      status: 'completed',
      category: 'swag_sale',
      metadata: {
        orderId: order.id,
        customerEmail: order.customerEmail,
        stripePaymentIntentId: order.stripePaymentIntentId,
      },
    },
  });

  // Update account balance
  await prisma.account.update({
    where: { id: revenueAccount.id },
    data: { balance: { increment: order.total } },
  });

  // Link transaction to order
  await prisma.swagOrder.update({
    where: { id: orderId },
    data: { transactionId: transaction.id },
  });

  await logAgentAction({
    context: { ...context, agentName: 'TransactionAgent' },
    actionType: 'create_transaction',
    targetEntity: 'transaction',
    targetId: transaction.id,
    inputData: { orderId, amount: order.total },
    outputData: { transactionId: transaction.id, accountId: revenueAccount.id },
    executionTimeMs: Date.now() - startTime,
  });

  return transaction.id;
}

async function updateAccountsReceivable(orderId: string, context: AgentContext): Promise<string> {
  const order = await prisma.swagOrder.findUnique({ where: { id: orderId } });
  if (!order || !order.invoiceId) throw new Error(`Order ${orderId} or invoice not found`);

  const startTime = Date.now();
  const invoice = await prisma.invoice.findUnique({ where: { id: order.invoiceId } });
  if (!invoice) throw new Error(`Invoice ${order.invoiceId} not found`);

  const ar = await prisma.accountsReceivable.create({
    data: {
      orderId: order.id,
      invoiceId: invoice.id,
      customerEmail: order.customerEmail,
      customerName: order.customerName,
      invoiceNumber: invoice.invoiceNumber,
      originalAmount: order.total,
      amountPaid: order.total, // Already paid
      amountDue: 0,
      status: 'paid',
      dueDate: invoice.dueDate,
      paidDate: new Date(),
    },
  });

  // Link AR to order
  await prisma.swagOrder.update({
    where: { id: orderId },
    data: { arRecordId: ar.id },
  });

  await logAgentAction({
    context: { ...context, agentName: 'ARAgent' },
    actionType: 'update_ar',
    targetEntity: 'ar',
    targetId: ar.id,
    inputData: { orderId, invoiceId: invoice.id },
    outputData: { arId: ar.id, status: 'paid', amountDue: 0 },
    executionTimeMs: Date.now() - startTime,
  });

  return ar.id;
}

export async function getExecutionStatus(orderId: string) {
  return prisma.orderToCashExecution.findUnique({
    where: { orderId },
  });
}

export async function resumeExecution(executionId: string): Promise<void> {
  const execution = await prisma.orderToCashExecution.findUnique({
    where: { id: executionId },
  });

  if (!execution || execution.status !== 'paused') {
    throw new Error('Execution not found or not paused');
  }

  await prisma.orderToCashExecution.update({
    where: { id: executionId },
    data: {
      status: 'active',
      humanInterventions: { increment: 1 },
    },
  });

  // Re-trigger the flow from current stage
  // This would need to be more sophisticated in a full implementation
}
