import db from '../config/database';
import { logger } from '../utils/logger';
import { B2BCustomer, TieredPrice, TieredPriceResult, CreditStatus, BatchOrderItem, BatchOrder } from '../models/B2B';
import { PricingService } from './PricingService';

export interface TieredPricingInput {
  productId: string;
  quantity: number;
  customerId: string;
  tenantId: string;
  shopId?: string;
  taskId?: string;
  traceId: string;
  businessType: 'TOC' | 'TOB';
}

export interface TieredPricingOutput {
  unitPrice: number;
  totalAmount: number;
  discountPercent: number;
  appliedTier: {
    minQuantity: number;
    maxQuantity?: number;
  };
  profitMargin: number;
  isApproved: boolean;
  rejectionReason?: string;
}

export class B2BTradeService {
  private static readonly PROFIT_MARGIN_THRESHOLD = 0.15;

  private static readonly DEFAULT_TIERED_PRICES: TieredPrice[] = [
    { product_id: '', tenant_id: '', min_quantity: 1, max_quantity: 9, price: 0, discount_percent: 0 },
    { product_id: '', tenant_id: '', min_quantity: 10, max_quantity: 49, price: 0, discount_percent: 5 },
    { product_id: '', tenant_id: '', min_quantity: 50, max_quantity: 99, price: 0, discount_percent: 10 },
    { product_id: '', tenant_id: '', min_quantity: 100, max_quantity: 499, price: 0, discount_percent: 15 },
    { product_id: '', tenant_id: '', min_quantity: 500, max_quantity: undefined, price: 0, discount_percent: 20 },
  ];

  static async calculateTieredPrice(
    input: TieredPricingInput
  ): Promise<TieredPricingOutput> {
    const { productId, quantity, customerId, tenantId, shopId, taskId, traceId, businessType } = input;

    logger.info(`[B2BTradeService] Calculating tiered price - productId: ${productId}, quantity: ${quantity}, customerId: ${customerId}, tenantId: ${tenantId}, traceId: ${traceId}`);

    try {
      const basePrice = await this.getProductBasePrice(productId, tenantId, traceId);
      
      const appliedTier = this.findApplicableTier(quantity);
      
      const discountMultiplier = 1 - (appliedTier.discount_percent / 100);
      const unitPrice = Number((basePrice * discountMultiplier).toFixed(2));
      const totalAmount = Number((unitPrice * quantity).toFixed(2));

      const costPrice = basePrice;
      const profitMargin = Number(((unitPrice - costPrice) / unitPrice).toFixed(4));

      const isApproved = profitMargin >= this.PROFIT_MARGIN_THRESHOLD;
      
      let rejectionReason: string | undefined;
      if (!isApproved) {
        rejectionReason = `Profit margin ${(profitMargin * 100).toFixed(2)}% is below the minimum threshold of ${(this.PROFIT_MARGIN_THRESHOLD * 100).toFixed(0)}% for B2B transactions`;
        
        logger.warn(`[B2BTradeService] Tiered price rejected due to low profit margin - productId: ${productId}, profitMargin: ${profitMargin}, threshold: ${this.PROFIT_MARGIN_THRESHOLD}`);
      }

      const result: TieredPricingOutput = {
        unitPrice,
        totalAmount,
        discountPercent: appliedTier.discount_percent,
        appliedTier: {
          minQuantity: appliedTier.min_quantity,
          maxQuantity: appliedTier.max_quantity,
        },
        profitMargin,
        isApproved,
        rejectionReason,
      };

      logger.info(`[B2BTradeService] Tiered price calculated - unitPrice: ${unitPrice}, totalAmount: ${totalAmount}, profitMargin: ${profitMargin}, isApproved: ${isApproved}`);

      return result;
    } catch (error: any) {
      logger.error(`[B2BTradeService] Tiered price calculation failed - error: ${error.message}`);
      throw error;
    }
  }

  static async createBatchOrder(
    customerId: string,
    items: BatchOrderItem[],
    options: {
      tenantId: string;
      shopId?: string;
      taskId?: string;
      traceId: string;
      businessType: 'TOC' | 'TOB';
    }
  ): Promise<BatchOrder> {
    const { tenantId, shopId, taskId, traceId, businessType } = options;

    logger.info(`[B2BTradeService] Creating batch order - customerId: ${customerId}, itemCount: ${items.length}, tenantId: ${tenantId}, traceId: ${traceId}`);

    try {
      const creditStatus = await this.checkCreditLimit(customerId, tenantId, traceId);
      
      if (!creditStatus.canPlaceOrder) {
        throw new Error(`Credit limit exceeded: ${creditStatus.reason}`);
      }

      let totalAmount = 0;
      for (const item of items) {
        const itemTotal = item.unit_price * item.quantity * (1 - (item.discount_percent || 0) / 100);
        totalAmount += itemTotal;
      }
      totalAmount = Number(totalAmount.toFixed(2));

      if (totalAmount > creditStatus.creditAvailable) {
        throw new Error(`Order amount ${totalAmount} exceeds available credit ${creditStatus.creditAvailable}`);
      }

      const order: BatchOrder = {
        tenant_id: tenantId,
        customer_id: customerId,
        total_amount: totalAmount,
        payment_status: 'PENDING',
        trace_id: traceId,
        task_id: taskId,
        business_type: 'TOB',
        items: items,
      };

      logger.info(`[B2BTradeService] Batch order created - customerId: ${customerId}, totalAmount: ${totalAmount}, tenantId: ${tenantId}`);

      return order;
    } catch (error: any) {
      logger.error(`[B2BTradeService] Batch order creation failed - error: ${error.message}`);
      throw error;
    }
  }

  static async checkCreditLimit(
    customerId: string,
    tenantId: string,
    traceId: string
  ): Promise<CreditStatus> {
    logger.info(`[B2BTradeService] Checking credit limit - customerId: ${customerId}, tenantId: ${tenantId}, traceId: ${traceId}`);

    const mockCustomer: B2BCustomer = {
      id: customerId,
      tenant_id: tenantId,
      company_name: 'Sample Company',
      credit_limit: 50000,
      credit_used: 15000,
      payment_terms: 30,
      tier_level: 'SILVER',
      status: 'ACTIVE',
      trace_id: traceId,
    };

    const creditLimit = mockCustomer.credit_limit;
    const creditUsed = mockCustomer.credit_used;
    const creditAvailable = creditLimit - creditUsed;

    const canPlaceOrder = mockCustomer.status === 'ACTIVE' && creditAvailable > 0;

    const result: CreditStatus = {
      customerId,
      creditLimit,
      creditUsed,
      creditAvailable,
      canPlaceOrder,
      reason: canPlaceOrder ? undefined : 'Customer is not active or has no available credit',
    };

    logger.info(`[B2BTradeService] Credit limit checked - creditLimit: ${creditLimit}, creditUsed: ${creditUsed}, creditAvailable: ${creditAvailable}, canPlaceOrder: ${canPlaceOrder}`);

    return result;
  }

  static async setPaymentTerms(
    customerId: string,
    terms: {
      days: number;
      autoApprove: boolean;
    },
    options: {
      tenantId: string;
      traceId: string;
    }
  ): Promise<{ customerId: string; days: number; status: 'ACTIVE' | 'SUSPENDED' | 'EXPIRED'; effectiveFrom: Date }> {
    const { tenantId, traceId } = options;

    logger.info(`[B2BTradeService] Setting payment terms - customerId: ${customerId}, days: ${terms.days}, tenantId: ${tenantId}, traceId: ${traceId}`);

    return {
      customerId,
      days: terms.days,
      status: 'ACTIVE',
      effectiveFrom: new Date(),
    };
  }

  private static async getProductBasePrice(productId: string, tenantId: string, traceId: string): Promise<number> {
    logger.info(`[B2BTradeService] Getting product base price - productId: ${productId}, tenantId: ${tenantId}`);

    return 100;
  }

  private static findApplicableTier(quantity: number): TieredPrice {
    for (const tier of this.DEFAULT_TIERED_PRICES) {
      if (quantity >= tier.min_quantity) {
        if (tier.max_quantity === undefined || quantity <= tier.max_quantity) {
          return tier;
        }
      }
    }
    return this.DEFAULT_TIERED_PRICES[this.DEFAULT_TIERED_PRICES.length - 1];
  }

  static async getBatchOrderById(orderId: string, tenantId: string): Promise<BatchOrder | null> {
    logger.info(`[B2BTradeService] Getting batch order by ID - orderId: ${orderId}, tenantId: ${tenantId}`);

    try {
      const order = await db('cf_b2b_batch_orders')
        .where({ id: orderId, tenant_id: tenantId })
        .first();

      if (!order) {
        return null;
      }

      if (typeof order.items === 'string') {
        order.items = JSON.parse(order.items);
      }

      return order as BatchOrder;
    } catch (error: any) {
      logger.error(`[B2BTradeService] Failed to get batch order - orderId: ${orderId}, error: ${error.message}`);
      throw error;
    }
  }

  static async updatePaymentStatus(
    orderId: string,
    status: 'PENDING' | 'PARTIAL' | 'PAID' | 'OVERDUE',
    options: {
      tenantId: string;
      shopId?: string;
      traceId: string;
    }
  ): Promise<void> {
    const { tenantId, traceId } = options;

    logger.info(`[B2BTradeService] Updating payment status - orderId: ${orderId}, status: ${status}, tenantId: ${tenantId}, traceId: ${traceId}`);

    try {
      await db('cf_b2b_batch_orders')
        .where({ id: orderId, tenant_id: tenantId })
        .update({
          payment_status: status,
          updated_at: new Date(),
        });

      logger.info(`[B2BTradeService] Payment status updated - orderId: ${orderId}, status: ${status}`);
    } catch (error: any) {
      logger.error(`[B2BTradeService] Failed to update payment status - orderId: ${orderId}, error: ${error.message}`);
      throw error;
    }
  }

  static async checkOverdueOrders(tenantId: string): Promise<string[]> {
    logger.info(`[B2BTradeService] Checking overdue orders - tenantId: ${tenantId}`);

    try {
      const overdueOrders = await db('cf_b2b_batch_orders')
        .where({ tenant_id: tenantId, payment_status: 'PENDING' })
        .where('due_date', '<', new Date())
        .select('id');

      const overdueIds = overdueOrders.map((order: any) => order.id);

      if (overdueIds.length > 0) {
        await db('cf_b2b_batch_orders')
          .whereIn('id', overdueIds)
          .update({ payment_status: 'OVERDUE', updated_at: new Date() });

        logger.info(`[B2BTradeService] Updated overdue orders - tenantId: ${tenantId}, overdueCount: ${overdueIds.length}`);
      }

      return overdueIds;
    } catch (error: any) {
      logger.error(`[B2BTradeService] Failed to check overdue orders - tenantId: ${tenantId}, error: ${error.message}`);
      throw error;
    }
  }

  static async createCustomer(
    customerData: Omit<B2BCustomer, 'id' | 'created_at' | 'updated_at'>,
    options: {
      tenantId: string;
      shopId?: string;
      traceId: string;
    }
  ): Promise<string> {
    const { tenantId, shopId, traceId } = options;

    logger.info(`[B2BTradeService] Creating B2B customer - companyName: ${customerData.company_name}, tenantId: ${tenantId}, traceId: ${traceId}`);

    try {
      const customerId = `B2B-CUST-${Date.now()}-${Math.floor(Math.random() * 10000)}`;

      const customer = {
        id: customerId,
        ...customerData,
        tenant_id: tenantId,
        trace_id: traceId,
        business_type: 'TOB',
        created_at: new Date(),
        updated_at: new Date(),
      };

      await db('cf_b2b_customers').insert(customer);

      logger.info(`[B2BTradeService] B2B customer created - customerId: ${customerId}, tenantId: ${tenantId}`);

      return customerId;
    } catch (error: any) {
      logger.error(`[B2BTradeService] Failed to create B2B customer - tenantId: ${tenantId}, error: ${error.message}`);
      throw error;
    }
  }

  static async createTieredPrice(
    priceData: Omit<TieredPrice, 'id' | 'created_at'>,
    options: {
      tenantId: string;
      shopId?: string;
      traceId: string;
    }
  ): Promise<string> {
    const { tenantId, shopId, traceId } = options;

    logger.info(`[B2BTradeService] Creating tiered price - productId: ${priceData.product_id}, tenantId: ${tenantId}, traceId: ${traceId}`);

    try {
      const priceId = `TIER-${Date.now()}-${Math.floor(Math.random() * 10000)}`;

      const tieredPrice = {
        id: priceId,
        ...priceData,
        tenant_id: tenantId,
        trace_id: traceId,
        created_at: new Date(),
      };

      await db('cf_b2b_tiered_prices').insert(tieredPrice);

      logger.info(`[B2BTradeService] Tiered price created - priceId: ${priceId}, productId: ${priceData.product_id}`);

      return priceId;
    } catch (error: any) {
      logger.error(`[B2BTradeService] Failed to create tiered price - tenantId: ${tenantId}, error: ${error.message}`);
      throw error;
    }
  }

  static async initTables(): Promise<void> {
    logger.info('[B2BTradeService] Initializing B2B tables');

    try {
      if (!(await db.schema.hasTable('cf_b2b_customers'))) {
        await db.schema.createTable('cf_b2b_customers', (table) => {
          table.string('id').primary();
          table.string('tenant_id').notNullable().index();
          table.string('company_name').notNullable();
          table.string('contact_name');
          table.string('contact_email');
          table.string('contact_phone');
          table.decimal('credit_limit', 10, 2).notNullable().defaultTo(0);
          table.decimal('credit_used', 10, 2).notNullable().defaultTo(0);
          table.integer('payment_terms').notNullable().defaultTo(30);
          table.enum('tier_level', ['BRONZE', 'SILVER', 'GOLD', 'PLATINUM']).notNullable().defaultTo('BRONZE');
          table.enum('status', ['ACTIVE', 'SUSPENDED', 'INACTIVE']).notNullable().defaultTo('ACTIVE');
          table.string('trace_id');
          table.string('business_type').defaultTo('TOB');
          table.timestamp('created_at').defaultTo(db.fn.now());
          table.timestamp('updated_at').defaultTo(db.fn.now());
        });
        logger.info('[B2BTradeService] Table cf_b2b_customers created');
      }

      if (!(await db.schema.hasTable('cf_b2b_tiered_prices'))) {
        await db.schema.createTable('cf_b2b_tiered_prices', (table) => {
          table.string('id').primary();
          table.string('product_id').notNullable().index();
          table.string('tenant_id').notNullable().index();
          table.integer('min_quantity').notNullable();
          table.integer('max_quantity');
          table.decimal('price', 10, 2).notNullable();
          table.decimal('discount_percent', 5, 2).notNullable().defaultTo(0);
          table.string('trace_id');
          table.timestamp('created_at').defaultTo(db.fn.now());
          table.index(['product_id', 'tenant_id', 'min_quantity']);
        });
        logger.info('[B2BTradeService] Table cf_b2b_tiered_prices created');
      }

      if (!(await db.schema.hasTable('cf_b2b_batch_orders'))) {
        await db.schema.createTable('cf_b2b_batch_orders', (table) => {
          table.string('id').primary();
          table.string('tenant_id').notNullable().index();
          table.string('customer_id').notNullable().index();
          table.decimal('total_amount', 10, 2).notNullable();
          table.enum('payment_status', ['PENDING', 'PARTIAL', 'PAID', 'OVERDUE']).notNullable().defaultTo('PENDING');
          table.date('due_date');
          table.json('items');
          table.string('trace_id');
          table.string('task_id');
          table.string('business_type').defaultTo('TOB');
          table.timestamp('created_at').defaultTo(db.fn.now());
          table.timestamp('updated_at').defaultTo(db.fn.now());
        });
        logger.info('[B2BTradeService] Table cf_b2b_batch_orders created');
      }

      logger.info('[B2BTradeService] B2B tables initialized successfully');
    } catch (error: any) {
      logger.error(`[B2BTradeService] Failed to initialize B2B tables - error: ${error.message}`);
      throw error;
    }
  }
}
