
import {
  pgEndorseIncomingCheck,
  pgGetInternalTransfers,
  pgGetMaterials,
  pgSaveInternalTransfers,
  pgSaveInventoryMovements,
  pgSaveMaterials,
  pgSaveShipments,
} from '@/lib/postgres/data-access';
import type { InternalTransferDocument, PurchaseOrderItem, Shipment } from '@/lib/types';
import type { LinkedShipment, PurchaseSideEffectsPort } from '../application/purchase-side-effects.port';

function generateTransferDocNumberFromExisting(docs: Array<Record<string, unknown>>) {
  const year = new Date().getFullYear();
  const prefix = `ITD/${year}/`;
  let maxSeq = 0;

  docs.forEach((doc) => {
    const transferNumber = String(doc.transferNumber || '').trim();
    if (!transferNumber.startsWith(prefix)) return;
    const parts = transferNumber.split('/');
    if (parts.length !== 3) return;
    const seq = Number.parseInt(parts[2], 10);
    if (Number.isFinite(seq)) {
      maxSeq = Math.max(maxSeq, seq);
    }
  });

  return `${prefix}${String(maxSeq + 1).padStart(4, '0')}`;
}

function isInventoryTrackedMaterial(material: Record<string, unknown> | undefined): boolean {
  const category = String(material?.itemCategory || '').trim();
  return category !== 'service' && category !== 'non_stock';
}

async function syncMaterialPrices(
  processedItems: PurchaseOrderItem[],
  taxMethod: 'tax-inclusive' | 'invoice-level' | 'line-level' | 'tax-exempt',
  calculatedTaxAmount: number,
  discountedSubTotal: number,
  currencyCode?: string
): Promise<void> {
  try {
    const materials = (((await pgGetMaterials({ page: 1, pageSize: 5000 })).items || []) as any[]);
    const materialsById = new Map(materials.map((m: any) => [m.id, m]));

    const materialCostAggregation = new Map<string, { totalQty: number; totalCostInclTax: number }>();

    processedItems.forEach((item) => {
      const material = materialsById.get(item.materialId);
      if (!isInventoryTrackedMaterial(material)) return;

      const quantity = Number(item.quantity || 0);
      if (!Number.isFinite(quantity) || quantity <= 0) return;

      const lineNet = Number(item.total || 0);
      if (!Number.isFinite(lineNet) || lineNet < 0) return;

      let lineTax = 0;
      if (taxMethod === 'line-level') {
        lineTax = Number(item.taxAmount || 0);
      } else if (taxMethod === 'invoice-level') {
        lineTax = discountedSubTotal > 0 ? calculatedTaxAmount * (lineNet / discountedSubTotal) : 0;
      }

      const lineGross = lineNet + (Number.isFinite(lineTax) && lineTax > 0 ? lineTax : 0);
      const current = materialCostAggregation.get(item.materialId) || { totalQty: 0, totalCostInclTax: 0 };
      current.totalQty += quantity;
      current.totalCostInclTax += lineGross;
      materialCostAggregation.set(item.materialId, current);
    });

    const roundTo2 = (value: number) => Math.round(value * 100) / 100;
    let hasChanges = false;

    materialCostAggregation.forEach((aggregate, materialId) => {
      if (!Number.isFinite(aggregate.totalQty) || aggregate.totalQty <= 0) return;
      const avgUnitCostInclTax = aggregate.totalCostInclTax / aggregate.totalQty;
      if (!Number.isFinite(avgUnitCostInclTax) || avgUnitCostInclTax <= 0) return;

      const material = materialsById.get(materialId);
      if (!material) return;

      material.purchasePrice = avgUnitCostInclTax;
      if (currencyCode) {
        (material as Record<string, unknown>).purchaseCurrencyCode = currencyCode;
      }

      if (material.enableProfitMargin) {
        const margin = Number(material.profitMargin || 0);
        const nextSalePrice = roundTo2(avgUnitCostInclTax * (1 + (Number.isFinite(margin) ? margin : 0) / 100));
        material.salePrice = Math.max(0.01, nextSalePrice);
      }

      hasChanges = true;
    });

    if (!hasChanges) {
      return;
    }

    await pgSaveMaterials(materials as Array<Record<string, unknown>>);
  } catch (error) {
    console.error('Failed to sync material prices from purchase order:', error);
  }
}

export class DefaultPurchaseSideEffectsPort implements PurchaseSideEffectsPort {
  async endorseIncomingChecksForSupplier(input: {
    paymentMethods: any[];
    supplierId: string;
    supplierName: string;
    orderNumber: string;
  }): Promise<boolean> {
    for (const method of input.paymentMethods) {
      if (method.method === 'check' && method.checkSource === 'incoming' && Number(method.amount || 0) > 0) {
        const selectedIncomingCheckIds = Array.isArray(method.incomingCheckIds) ? method.incomingCheckIds : [];
        for (const checkId of selectedIncomingCheckIds) {
          const done = await pgEndorseIncomingCheck(
                checkId,
                'endorsed-to-party',
                input.supplierId,
                input.supplierName,
                'supplier',
                `Purchase Order #${input.orderNumber}`
              );

          if (!done) return false;
        }
      }
    }

    return true;
  }

  async applyInventoryForPostedOrder(input: {
    order: { id: string; date: string };
    items: PurchaseOrderItem[];
    periodCloseDate?: string;
    inventoryCloseDate?: string;
  }): Promise<LinkedShipment> {
    let linkedShipment: LinkedShipment = null;
    const materials = (((await pgGetMaterials({ page: 1, pageSize: 5000 })).items || []) as Array<Record<string, unknown>>);
    const materialsById = new Map(materials.map((entry) => [String(entry.id || ''), entry]));
    const inventoryItems = input.items.filter((item) => isInventoryTrackedMaterial(materialsById.get(String(item.materialId || ''))));

    if (inventoryItems.length === 0) {
      return null;
    }
    try {
      const shipmentId = `shp-${Date.now()}-${Math.floor(Math.random() * 1_000_000)}`;
      const shipment = {
        id: shipmentId,
        date: input.order.date,
        shipmentType: 'adjustment' as const,
        status: 'completed' as const,
        invoiceId: input.order.id,
        hasInventoryMovement: true,
        periodCloseDate: input.periodCloseDate,
        inventoryCloseDate: input.inventoryCloseDate,
      };
      linkedShipment = { id: shipment.id, shipmentType: shipment.shipmentType };
      await pgSaveShipments([shipment as unknown as Record<string, unknown>]);
    } catch (error) {
      console.error('Failed to create linked shipment for purchase order:', error);
    }

    try {
      const movementEntries = inventoryItems.map((item, index) => ({
        id: `inv-move-${Date.now()}-${index + 1}`,
        date: input.order.date,
        materialId: item.materialId,
        warehouseId: item.warehouseId || '',
        shelfId: item.shelfId || '',
        quantityIn: item.quantity,
        quantityOut: 0,
        referenceType: 'purchase_order',
        referenceId: input.order.id,
        shipmentId: linkedShipment?.id,
        invoiceId: input.order.id,
      }));

      await pgSaveInventoryMovements(movementEntries as Array<Record<string, unknown>>);
    } catch (error) {
      console.error('Failed to record inventory movements for PO:', error);
    }

    return linkedShipment;
  }

  async createShelfDistributionTask(input: {
    order: { id: string; orderNumber: string };
    items: PurchaseOrderItem[];
    supplierId: string;
    supplierName: string;
    warehouseId?: string;
    settings: Record<string, unknown>;
    createdBy: string;
    transferCreationMode?: 'direct_internal_transfer' | 'work_task' | 'none';
  }): Promise<void> {
    try {
      const warehousesEnabled = input.settings.warehousesEnabled === true || input.settings.warehousesEnabled === 'true';
      const purchaseShelfTaskEnabled =
        input.settings.purchaseShelfTaskEnabled === true || input.settings.purchaseShelfTaskEnabled === 'true';
      const purchaseShipmentWorkflowEnabled =
        input.settings.purchaseShipmentWorkflowEnabled === true || input.settings.purchaseShipmentWorkflowEnabled === 'true';
      const explicitMode = input.transferCreationMode;
      const defaultPurchaseShelfId = String(input.settings.defaultPurchaseShelfId || '').trim();
      const headerWarehouseId = String(input.warehouseId || '').trim();
      const taskWarehouseId = headerWarehouseId || String(input.settings.defaultPurchaseWarehouseId || '').trim();

      if (explicitMode === 'none') {
        return;
      }

      if (warehousesEnabled && purchaseShelfTaskEnabled && taskWarehouseId) {
        const materials = (((await pgGetMaterials({ page: 1, pageSize: 5000 })).items || []) as Array<Record<string, unknown>>);
        const materialsById = new Map(materials.map((entry) => [String(entry.id || ''), entry]));

        const transferItems = input.items
          .filter((item) => {
            const quantity = Number(item.quantity || 0);
            return Number.isFinite(quantity) && quantity > 0 && isInventoryTrackedMaterial(materialsById.get(String(item.materialId || '')));
          })
          .map((item, index) => {
            const resolvedWarehouseId = String(item.warehouseId || headerWarehouseId || taskWarehouseId).trim();
            const resolvedTargetShelfId = String(item.shelfId || '').trim();
            const sourceShelfId = defaultPurchaseShelfId || resolvedTargetShelfId;

            return {
              lineId: `po-${input.order.orderNumber}-${index + 1}`,
              materialId: item.materialId,
              quantity: item.quantity,
              // Explicit mode overrides settings. Fallback keeps previous behavior.
              receivedQuantity: explicitMode === 'work_task'
                ? 0
                : explicitMode === 'direct_internal_transfer'
                  ? item.quantity
                  : (purchaseShipmentWorkflowEnabled ? 0 : item.quantity),
              fromWarehouseId: resolvedWarehouseId,
              fromShelfId: sourceShelfId,
              toWarehouseId: resolvedWarehouseId,
              toShelfId: resolvedTargetShelfId || sourceShelfId,
              note: `Purchase Order ${input.order.orderNumber}`,
            };
          })
          .filter((item) => item.fromWarehouseId && item.fromShelfId && item.toWarehouseId && item.toShelfId);

        if (transferItems.length === 0) {
          return;
        }

        // Explicit mode overrides settings. Fallback keeps previous behavior.
        const receiptStatus: 'pending' | 'received' = explicitMode === 'work_task'
          ? 'pending'
          : explicitMode === 'direct_internal_transfer'
            ? 'received'
            : (purchaseShipmentWorkflowEnabled ? 'pending' : 'received');

        
        const { randomUUID } = await import('crypto');
        const existingTransfers = (((await pgGetInternalTransfers({ page: 1, pageSize: 10000 })).items || []) as Array<Record<string, unknown>>);
        const transferDoc: InternalTransferDocument = {
        date: new Date().toISOString(),
        enableWarehouseTransfer: true,
        enableShelfTransfer: true,
        allowZeroItems: false,
        supplierId: input.supplierId,
        supplierName: input.supplierName,
        sourcePurchaseOrderId: input.order.id,
        sourcePurchaseOrderNumber: input.order.orderNumber,
        items: transferItems,
        note: `Shelf distribution for Purchase Order ${input.order.orderNumber}`,
        receiptStatus,
        createdBy: input.createdBy,
        id: `itd-${randomUUID().slice(0, 8)}`,
        transferNumber: generateTransferDocNumberFromExisting(existingTransfers),
        status: 'active',
        createdAt: new Date().toISOString(),
        };
        await pgSaveInternalTransfers([transferDoc as unknown as Record<string, unknown>]);
        
      }
    } catch (error) {
      console.error('Failed to create shelf distribution task:', error);
    }
  }

  async syncMaterialPricesFromPurchase(input: {
    processedItems: PurchaseOrderItem[];
    taxMethod: 'tax-inclusive' | 'invoice-level' | 'line-level' | 'tax-exempt';
    calculatedTaxAmount: number;
    discountedSubTotal: number;
    currencyCode?: string;
  }): Promise<void> {
    await syncMaterialPrices(
      input.processedItems,
      input.taxMethod,
      input.calculatedTaxAmount,
      input.discountedSubTotal,
      input.currencyCode
    );
  }
}

export function createDefaultPurchaseSideEffectsPort(): PurchaseSideEffectsPort {
  return new DefaultPurchaseSideEffectsPort();
}
