import { BN } from '@ocap/util';
import { Customer, PaymentMethod } from '../../store/models';
import { CheckoutSession } from '../../store/models/checkout-session';
import { PaymentIntent } from '../../store/models/payment-intent';
import { Payout } from '../../store/models/payout';
import { ProductVendor } from '../../store/models/product-vendor';
import env from '../env';
import logger from '../logger';
import { calculateVendorCommission, VendorAdapterFactory } from './adapters/factory';

const DEFAULT_COMMISSION_RATE = 20;

export interface VendorFulfillmentResult {
  vendorId: string;
  vendorKey: string;
  orderId: string;
  status: 'pending' | 'processing' | 'completed' | 'failed';
  commissionAmount: string;
  commissionType: 'percentage' | 'fixed_amount';
  commissionRate: number;
  productId?: string; // Add product ID for multi-product tracking
  errorMessage?: string;
  serviceUrl?: string;
  estimatedTime?: number;
  vendorOrderId?: string;
  progress?: number;
  orderDetails?: any;
  installationInfo?: any;
}

export interface MultiVendorFulfillmentResult {
  checkoutSessionId: string;
  success: boolean;
  results: VendorFulfillmentResult[];
  totalCommissionAmount: string;
  errorMessage?: string;
}

export class VendorFulfillmentService {
  // Execute single vendor fulfillment (public interface for coordinator system)
  static async fulfillSingleVendorOrder(
    orderInfo: {
      checkoutSessionId: string;
      amount_total: string;
      customer_id: string;
      invoiceId: string;
      currency_id: string;
      customer_did: string;
    },
    vendorConfig: any,
    sharedContext?: any // Pass bindDomainCap and other shared data between vendors
  ): Promise<VendorFulfillmentResult> {
    try {
      const vendor = await ProductVendor.findByPk(vendorConfig.vendor_id);

      if (!vendor) {
        throw new Error(`Vendor not found: ${vendorConfig.vendor_id}`);
      }

      const adapter = await this.getVendorAdapter(vendor.vendor_key);

      // Calculate commission amount
      const commissionRate = vendorConfig.commission_rate ?? DEFAULT_COMMISSION_RATE;
      const commissionType = vendorConfig.commission_type || 'percentage';
      const commissionAmount = calculateVendorCommission(
        orderInfo.amount_total,
        commissionRate,
        commissionType,
        vendorConfig.amount
      );

      const userEmail = (await Customer.findByPk(orderInfo.customer_id))?.email || '';

      const fulfillmentResult = await adapter.fulfillOrder({
        checkoutSessionId: orderInfo.checkoutSessionId,
        productCode: vendorConfig.vendor_id,
        customerId: orderInfo.customer_id || '',
        quantity: 1,
        invoiceId: orderInfo.invoiceId,
        amount: orderInfo.amount_total,
        currency: orderInfo.currency_id,

        description: `This is an app launched by ${env.appName || 'Third Party'}`,
        userInfo: {
          userDid: orderInfo.customer_did!,
          email: userEmail,
          description: `This is an app launched by ${env.appName || 'Third Party'}`,
        },
        deliveryParams: {
          blockletMetaUrl: vendor.metadata?.blockletMetaUrl,
          customParams: {
            ...vendorConfig.custom_params,
            // Merge shared context (bindDomainCap from didnames-adapter)
            ...(sharedContext || {}),
          },
        },
      });

      logger.info('Single vendor fulfillment completed', {
        vendorId: vendorConfig.vendor_id,
        orderId: fulfillmentResult.orderId,
        status: fulfillmentResult.status,
        commissionAmount,
      });

      return {
        vendorId: vendorConfig.vendor_id,
        vendorKey: vendorConfig.vendor_id,
        orderId: fulfillmentResult.orderId,
        status: fulfillmentResult.status,
        commissionAmount,
        commissionType,
        commissionRate,
        errorMessage: fulfillmentResult.message,
        serviceUrl: fulfillmentResult.serviceUrl,
        estimatedTime: fulfillmentResult.estimatedTime,
        vendorOrderId: fulfillmentResult.vendorOrderId,
        progress: fulfillmentResult.progress,
        orderDetails: fulfillmentResult.orderDetails,
        installationInfo: fulfillmentResult.installationInfo,
      };
    } catch (error: any) {
      logger.error('Single vendor fulfillment failed', {
        vendorId: vendorConfig.vendor_id,
        error,
      });

      throw error;
    }
  }

  static async createVendorPayouts(checkoutSession: CheckoutSession, paymentIntent: PaymentIntent): Promise<void> {
    try {
      const paymentMethodId = paymentIntent.payment_method_id;
      const paymentIntentId = paymentIntent.id;

      // If fulfillmentResults not provided, calculate commission info
      const commissionData = checkoutSession.vendor_info?.map((vendorInfo: any) => ({
        vendorId: vendorInfo.vendor_id,
        orderId: vendorInfo.order_id,
        status: vendorInfo.status,
        commissionAmount: vendorInfo.commissionAmount,
      })) as VendorFulfillmentResult[];

      const payoutPromises = commissionData
        .filter((result) => result.status !== 'failed' && new BN(result.commissionAmount).gt(new BN('0')))
        .map(async (result) => {
          const vendor = await ProductVendor.findByPk(result.vendorId);
          const paymentMethod = await PaymentMethod.findByPk(paymentMethodId);

          const appPid = vendor?.app_pid;
          const destination = appPid || vendor?.metadata?.wallet_address || vendor?.metadata?.destination || '';

          await Payout.create({
            livemode: checkoutSession.livemode,
            automatic: true,
            description: `Vendor commission for ${result.vendorId}${appPid ? ` (${appPid})` : ''}`,
            destination,
            amount: result.commissionAmount,
            currency_id: checkoutSession.currency_id,
            customer_id: '',
            payment_intent_id: paymentIntentId,
            payment_method_id: paymentMethodId,
            status: paymentMethod?.type === 'stripe' ? 'deferred' : 'pending',
            attempt_count: 0,
            attempted: false,
            vendor_info: {
              vendor_id: result.vendorId,
              app_pid: appPid,
              order_id: result.orderId,
              commission_amount: result.commissionAmount,
            },
          });
        });

      await Promise.all(payoutPromises);

      logger.info('Vendor payouts created', {
        checkoutSessionId: checkoutSession.id,
        paymentIntentId: paymentIntent.id,
        payoutCount: commissionData.filter((r) => r.status !== 'failed').length,
      });
    } catch (error: any) {
      logger.error('Failed to create vendor payouts', {
        checkoutSessionId: checkoutSession.id,
        paymentIntentId: paymentIntent.id,
        error: error.message,
      });
      throw error;
    }
  }

  static async getVendorAdapter(vendorKey: string) {
    try {
      const vendor = await VendorAdapterFactory.create(vendorKey);
      return vendor;
    } catch (error: any) {
      logger.error('Failed to get vendor adapter', {
        vendorKey,
        error,
      });
      throw error;
    }
  }
}
