<?php

namespace WMS\Controllers;

use WMS\Core\Database;
use WMS\Core\Request;
use WMS\Core\Response;
use WMS\Core\HanaDatabase;
use WMS\Integrations\Sap\HanaQueryService;
use WMS\Integrations\Sap\SapServiceLayerClient;

class SapController
{
    /**
     * GET /api/sap/purchase-orders
     * Lista órdenes de compra abiertas desde HANA.
     */
    public static function purchaseOrders(array $params): void
    {
        try {
            $sl = new SapServiceLayerClient();
            $sl->login();
            $r = $sl->getOpenPurchaseOrders();
            $orders = $r['value'] ?? $r;
            Response::success($orders);
        } catch (\Throwable $e) {
            Response::error('SAP no disponible: ' . $e->getMessage(), 503);
        }
    }

    /**
     * GET /api/sap/purchase-orders/{docEntry}/lines
     * Líneas de una orden de compra desde HANA.
     */
    public static function purchaseOrderLines(array $params): void
    {
        try {
            $docEntry = (int) $params['docEntry'];
            $sl = new SapServiceLayerClient();
            $sl->login();
            $doc = $sl->getPurchaseOrder($docEntry);
            $lines = $doc['DocumentLines'] ?? [];
            Response::success($lines);
        } catch (\Throwable $e) {
            Response::error('SAP no disponible: ' . $e->getMessage(), 503);
        }
    }

    /**
     * GET /api/sap/sales-orders
     * Lista pedidos de venta abiertos desde HANA.
     */
    public static function salesOrders(array $params): void
    {
        try {
            $sl = new SapServiceLayerClient();
            $sl->login();
            $r = $sl->getOpenSalesOrders();
            $orders = $r['value'] ?? $r;
            Response::success($orders);
        } catch (\Throwable $e) {
            Response::error('SAP no disponible: ' . $e->getMessage(), 503);
        }
    }

    /**
     * GET /api/sap/sales-orders/{docEntry}/lines
     * Líneas de un pedido de venta desde HANA.
     */
    public static function salesOrderLines(array $params): void
    {
        try {
            $docEntry = (int) $params['docEntry'];
            $sl = new SapServiceLayerClient();
            $sl->login();
            $doc = $sl->getSalesOrder($docEntry);
            $lines = $doc['DocumentLines'] ?? [];
            Response::success($lines);
        } catch (\Throwable $e) {
            Response::error('SAP no disponible: ' . $e->getMessage(), 503);
        }
    }

    /**
     * GET /api/sap/stock-transfers
     * Lista transferencias de stock desde SAP via Service Layer.
     */
    public static function stockTransfers(array $params): void
    {
        try {
            $sl = new SapServiceLayerClient();
            $sl->login();
            $r = $sl->getStockTransfers();
            $transfers = $r['value'] ?? $r;
            Response::success($transfers);
        } catch (\Throwable $e) {
            Response::error('SAP no disponible: ' . $e->getMessage(), 503);
        }
    }

    /**
     * GET /api/sap/goods-receipts
     * Lista entradas de mercaderia (PurchaseDeliveryNotes) desde SAP via Service Layer.
     */
    public static function goodsReceipts(array $params): void
    {
        try {
            $sl = new SapServiceLayerClient();
            $sl->login();
            $r = $sl->getGoodsReceiptPO();
            $receipts = $r['value'] ?? $r;
            Response::success($receipts);
        } catch (\Throwable $e) {
            Response::error('SAP no disponible: ' . $e->getMessage(), 503);
        }
    }

    /**
     * GET /api/sap/sync-log
     * Devuelve los ultimos registros del log de sync WMS<->SAP.
     */
    public static function syncLog(array $params): void
    {
        try {
            $limit = max(1, min((int)(Request::query('limit', 50)), 200));
            $type  = Request::query('type');
            $db = \WMS\Core\Database::getConnection();
            $where = ''; $args = [];
            if ($type) { $where = 'WHERE sync_type = ?'; $args[] = $type; }
            $sql = "SELECT id, sync_type, direction, sap_doc_type, sap_doc_entry,
                           sap_doc_num, wms_reference_id, status,
                           SUBSTRING(error_message,1,500) AS error_message,
                           retry_count, created_at
                    FROM sap_sync_log
                    {$where}
                    ORDER BY id DESC
                    LIMIT {$limit}";
            $st = $db->prepare($sql);
            $st->execute($args);
            Response::success($st->fetchAll(\PDO::FETCH_ASSOC));
        } catch (\Throwable $e) {
            Response::error('Error leyendo sync log: ' . $e->getMessage(), 500);
        }
    }

    /**
     * GET /api/sap/drivers
     * Lista choferes desde el UDO CENT_CHOFERES.
     */
    public static function drivers(array $params): void
    {
        try {
            $sl = new SapServiceLayerClient();
            $r  = $sl->get("/CENT_CHOFERES?\$select=Code,U_dNomChof,U_dNumIDChof,Canceled&\$top=200");
            $list = array_filter(($r['value'] ?? $r), fn($d) => ($d['Canceled'] ?? 'N') === 'N');
            $list = array_map(fn($d) => [
                'code'    => $d['Code'],
                'doc'     => $d['U_dNumIDChof'] ?? null,
                'name'    => $d['U_dNomChof'] ?? null,
            ], array_values($list));
            Response::success($list);
        } catch (\Throwable $e) {
            Response::error('SAP no disponible: ' . $e->getMessage(), 503);
        }
    }

    /**
     * GET /api/sap/vehicles
     * Lista vehiculos desde el UDO CENT_VEHICULOS.
     */
    public static function vehicles(array $params): void
    {
        try {
            $sl = new SapServiceLayerClient();
            $r  = $sl->get("/CENT_VEHICULOS?\$select=Code,U_dMarVeh,U_dNroMatVeh,U_dNomTrans,Canceled&\$top=200");
            $list = array_filter(($r['value'] ?? $r), fn($d) => ($d['Canceled'] ?? 'N') === 'N');
            $list = array_map(fn($d) => [
                'code'    => $d['Code'],
                'plate'   => $d['U_dNroMatVeh'] ?? null,
                'brand'   => $d['U_dMarVeh'] ?? null,
                'carrier' => $d['U_dNomTrans'] ?? null,
            ], array_values($list));
            Response::success($list);
        } catch (\Throwable $e) {
            Response::error('SAP no disponible: ' . $e->getMessage(), 503);
        }
    }

    /**
     * GET /api/sap/items
     * Lista artículos desde HANA.
     */
    public static function items(array $params): void
    {
        try {
            $top  = (int) (Request::query('top', 500));
            $skip = (int) (Request::query('skip', 0));
            try {
                $hana = new HanaQueryService();
                $items = $hana->getItems($top, $skip);
            } catch (\Throwable $e) {
                $sl = new SapServiceLayerClient();
                $sl->login();
                $r = $sl->getItems($top, $skip);
                $items = $r['value'] ?? $r;
            }
            Response::success($items);
        } catch (\Throwable $e) {
            Response::error('SAP no disponible: ' . $e->getMessage(), 503);
        }
    }

    /**
     * POST /api/sap/sync-items
     * Importa artículos de SAP HANA a la tabla items de WMS (upsert por item_code).
     */
    public static function syncItems(array $params): void
    {
        try {
            // Try HANA first, fall back to Service Layer
            $sapItems = [];
            $source = 'unknown';
            try {
                $hana = new HanaQueryService();
                $sapItems = $hana->getItems(10000, 0);
                $source = 'HANA';
            } catch (\Throwable $e) {
                // HANA unavailable, use Service Layer (paginate to get all)
                $sl = new SapServiceLayerClient();
                $sl->login();
                $sapItems = [];
                $skip = 0;
                $pageSize = 20;
                do {
                    $response = $sl->getItems($pageSize, $skip);
                    $page = $response['value'] ?? $response;
                    if (!is_array($page) || empty($page)) break;
                    $sapItems = array_merge($sapItems, $page);
                    $skip += count($page);
                } while (count($page) >= $pageSize);
                $source = 'ServiceLayer';
            }

            $db = Database::getConnection();
            $db->beginTransaction();

            $upsertSql = "INSERT INTO items (item_code, item_name, barcode, uom, requires_lot, requires_serial, item_group, sap_item_code)
                          VALUES (?, ?, ?, ?, ?, ?, ?, ?)
                          ON DUPLICATE KEY UPDATE
                              item_name = VALUES(item_name),
                              barcode = VALUES(barcode),
                              uom = VALUES(uom),
                              requires_lot = VALUES(requires_lot),
                              requires_serial = VALUES(requires_serial),
                              item_group = VALUES(item_group),
                              sap_item_code = VALUES(sap_item_code)";
            $stmt = $db->prepare($upsertSql);

            $synced = 0;
            foreach ($sapItems as $item) {
                // Map fields: HANA uses CodeBars/InvntryUom/ManBtchNum, SL uses BarCode/InventoryUOM/ManageBatchNumbers
                $code = $item['ItemCode'];
                $name = $item['ItemName'];
                $barcode = $item['CodeBars'] ?? $item['BarCode'] ?? null;
                $uom = $item['InvntryUom'] ?? $item['InventoryUOM'] ?? 'UN';
                $batch = $item['ManBtchNum'] ?? $item['ManageBatchNumbers'] ?? 'tNO';
                $serial = $item['ManSerNum'] ?? $item['ManageSerialNumbers'] ?? 'tNO';
                $group = $item['ItmsGrpNam'] ?? $item['ItemsGroupCode'] ?? null;

                $reqLot = ($batch === 'Y' || $batch === 'tYES') ? 1 : 0;
                $reqSerial = ($serial === 'Y' || $serial === 'tYES') ? 1 : 0;

                $stmt->execute([$code, $name, $barcode, $uom, $reqLot, $reqSerial, $group, $code]);
                $synced++;
            }

            $db->commit();

            Response::success([
                'message' => "Sincronizacion completada via {$source}",
                'synced'  => $synced,
            ]);
        } catch (\Throwable $e) {
            if (isset($db) && $db->inTransaction()) {
                $db->rollBack();
            }
            Response::error('Error al sincronizar articulos: ' . $e->getMessage(), 500);
        }
    }

    /**
     * POST /api/sap/sync-warehouses
     * Importa almacenes y bins de SAP HANA a WMS (upsert por code).
     */
    public static function syncWarehouses(array $params): void
    {
        try {
            // Try HANA first, fall back to Service Layer
            $sapWarehouses = [];
            $hanaAvailable = false;
            try {
                $hana = new HanaQueryService();
                $sapWarehouses = $hana->getWarehouses();
                $hanaAvailable = true;
            } catch (\Throwable $e) {
                $sl = new SapServiceLayerClient();
                $sl->login();
                $response = $sl->getWarehouses();
                $sapWarehouses = [];
                $raw = $response['value'] ?? $response;
                foreach ($raw as $w) {
                    $sapWarehouses[] = [
                        'WhsCode' => $w['WarehouseCode'] ?? $w['WhsCode'] ?? '',
                        'WhsName' => $w['WarehouseName'] ?? $w['WhsName'] ?? '',
                        'Street'  => $w['Street'] ?? null,
                    ];
                }
            }

            $db = Database::getConnection();
            $db->beginTransaction();

            $upsertWhSql = "INSERT INTO warehouses (code, name, address, sap_warehouse_code)
                            VALUES (?, ?, ?, ?)
                            ON DUPLICATE KEY UPDATE
                                name = VALUES(name),
                                address = VALUES(address),
                                sap_warehouse_code = VALUES(sap_warehouse_code)";
            $whStmt = $db->prepare($upsertWhSql);

            $upsertBinSql = "INSERT INTO bins (warehouse_id, code, aisle, rack, level, position, sap_bin_code)
                             VALUES (?, ?, ?, ?, ?, ?, ?)
                             ON DUPLICATE KEY UPDATE
                                 aisle = VALUES(aisle),
                                 rack = VALUES(rack),
                                 level = VALUES(level),
                                 position = VALUES(position),
                                 sap_bin_code = VALUES(sap_bin_code)";
            $binStmt = $db->prepare($upsertBinSql);

            $whSynced  = 0;
            $binSynced = 0;

            foreach ($sapWarehouses as $wh) {
                $whStmt->execute([
                    $wh['WhsCode'],
                    $wh['WhsName'],
                    $wh['Street'] ?? null,
                    $wh['WhsCode'],
                ]);
                $whSynced++;

                // Obtener el ID del almacén (ya sea insertado o existente)
                $whIdStmt = $db->prepare("SELECT id FROM warehouses WHERE code = ?");
                $whIdStmt->execute([$wh['WhsCode']]);
                $whId = (int) $whIdStmt->fetchColumn();

                // Sincronizar bins del almacen (solo si HANA disponible)
                if (!$hanaAvailable) { continue; }
                $sapBins = $hana->getBinLocations($wh['WhsCode']);
                foreach ($sapBins as $bin) {
                    $binStmt->execute([
                        $whId,
                        $bin['BinCode'],
                        $bin['Aisle'] ?? null,
                        $bin['Rack'] ?? null,
                        $bin['Level'] ?? null,
                        $bin['Position'] ?? null,
                        $bin['BinCode'],
                    ]);
                    $binSynced++;
                }
            }

            $db->commit();

            Response::success([
                'message'          => 'Sincronización de almacenes completada',
                'warehouses_synced' => $whSynced,
                'bins_synced'       => $binSynced,
            ]);
        } catch (\Throwable $e) {
            if (isset($db) && $db->inTransaction()) {
                $db->rollBack();
            }
            Response::error('Error al sincronizar almacenes: ' . $e->getMessage(), 500);
        }
    }

    /**
     * GET /api/sap/connection-status
     * Prueba conectividad con SAP HANA y Service Layer.
     */
    public static function connectionStatus(array $params): void
    {
        $hanaStatus = ['connected' => false];
        $slStatus   = ['connected' => false];

        // HANA deshabilitado: el driver ODBC segfaultea al cerrar la conexion
        // y mata el proceso PHP. El WMS opera 100% via Service Layer.
        $hanaStatus['connected'] = false;
        $hanaStatus['error'] = 'HANA no utilizado (operacion via Service Layer)';

        // Test Service Layer
        try {
            $sl = new SapServiceLayerClient();
            $sl->login();
            $slStatus['connected'] = true;
        } catch (\Throwable $e) {
            $slStatus['error'] = $e->getMessage();
        }

        Response::success([
            'hana'          => $hanaStatus,
            'service_layer' => $slStatus,
        ]);
    }

    /**
     * GET /api/sap/credit-notes  (Notas de Credito ORIN)
     */
    public static function creditNotes(array $params): void
    {
        try {
            $sl = new SapServiceLayerClient();
            $sl->login();
            $r = $sl->getCreditNotes();
            $data = $r['value'] ?? $r;
            Response::success($data);
        } catch (\Throwable $e) {
            Response::error('SAP no disponible: ' . $e->getMessage(), 503);
        }
    }

    /**
     * GET /api/sap/transfer-requests  (Solicitudes de Traslado OWTQ)
     */
    public static function transferRequests(array $params): void
    {
        try {
            $sl = new SapServiceLayerClient();
            $sl->login();
            $r = $sl->getTransferRequests();
            $data = $r['value'] ?? $r;
            Response::success($data);
        } catch (\Throwable $e) {
            Response::error('SAP no disponible: ' . $e->getMessage(), 503);
        }
    }

    /**
     * GET /api/sap/warehouses
     * Lista los almacenes de SAP (code + nombre) para los selectores de origen/destino.
     */
    public static function warehouses(array $params): void
    {
        try {
            $sl = new SapServiceLayerClient();
            $sl->login();
            $r = $sl->getWarehouses();
            $rows = $r['value'] ?? $r;
            $out = [];
            foreach ($rows as $w) {
                $out[] = [
                    'code' => $w['WarehouseCode'] ?? '',
                    'name' => $w['WarehouseName'] ?? '',
                ];
            }
            Response::success($out);
        } catch (\Throwable $e) {
            Response::error('SAP no disponible: ' . $e->getMessage(), 503);
        }
    }

    /**
     * POST /api/sap/transfer-requests
     * Crea una Solicitud de Traslado (InventoryTransferRequests / OWTQ) en SAP B1.
     * Body: { fromWarehouse, toWarehouse, dueDate?, comments?, lines:[{itemCode, quantity}] }
     */
    public static function createTransferRequest(array $params): void
    {
        $from = trim((string) Request::get('fromWarehouse', ''));
        $to   = trim((string) Request::get('toWarehouse', ''));
        $dueDate  = trim((string) Request::get('dueDate', ''));
        $comments = trim((string) Request::get('comments', ''));
        $linesIn  = Request::get('lines', []);

        // --- Validaciones ---
        if ($from === '' || $to === '') {
            Response::error('Debe indicar almacén de origen y destino.', 422); return;
        }
        if ($from === $to) {
            Response::error('El almacén de origen y destino no pueden ser el mismo.', 422); return;
        }
        if (!is_array($linesIn) || count($linesIn) === 0) {
            Response::error('Debe agregar al menos una línea (item y cantidad).', 422); return;
        }

        $db = Database::getConnection();
        $lines = [];
        $lotNotes = [];   // lotes solicitados por línea (van al Comments del doc)
        foreach ($linesIn as $i => $ln) {
            $itemId = (int) ($ln['itemId'] ?? 0);
            $icRaw  = trim((string) ($ln['itemCode'] ?? ''));
            $qty    = (float) ($ln['quantity'] ?? 0);
            $batch  = trim((string) ($ln['batchNumber'] ?? ''));

            // El producto DEBE existir en el maestro de items del WMS.
            if ($itemId > 0) {
                $st = $db->prepare("SELECT item_code, sap_item_code FROM items WHERE id = ? LIMIT 1");
                $st->execute([$itemId]);
            } else {
                $st = $db->prepare("SELECT item_code, sap_item_code FROM items WHERE item_code = ? OR sap_item_code = ? LIMIT 1");
                $st->execute([$icRaw, $icRaw]);
            }
            $row = $st->fetch(\PDO::FETCH_ASSOC);
            if (!$row) {
                $ref = $icRaw !== '' ? $icRaw : ('#' . $itemId);
                Response::error('La línea ' . ($i + 1) . ": el producto \"{$ref}\" no existe en el WMS.", 422); return;
            }
            if ($qty <= 0) {
                Response::error('La línea ' . ($i + 1) . ' debe tener cantidad mayor a 0.', 422); return;
            }
            $sapCode = ($row['sap_item_code'] ?? '') !== '' ? $row['sap_item_code'] : $row['item_code'];

            $line = [
                'ItemCode'          => $sapCode,
                'Quantity'          => $qty,
                'WarehouseCode'     => $to,    // destino
                'FromWarehouseCode' => $from,  // origen
            ];
            if ($batch !== '') {
                // SAP B1 NO persiste lote en una Solicitud de Traslado (OWTQ): la
                // asignación de lote ocurre recién en el Traslado real, y la línea del
                // pedido no tiene campo de texto (FreeText/BatchNumbers se rechazan o
                // ignoran). Para que el lote solicitado igual viaje y sea visible en SAP,
                // lo consolidamos en los Comentarios del documento.
                $lotNotes[] = $sapCode . ' → lote ' . $batch . ' x' . ($qty == (int)$qty ? (int)$qty : $qty);
            }
            $lines[] = $line;
        }

        $today = date('Y-m-d');
        // Normalizar dueDate (acepta YYYY-MM-DD); si no es válida usa hoy
        $due = (preg_match('/^\d{4}-\d{2}-\d{2}$/', $dueDate)) ? $dueDate : $today;

        // Comentarios + lotes solicitados (SAP no guarda lote por línea en una ST)
        $commentsFinal = $comments !== '' ? $comments : 'Solicitud de traslado creada desde WMS';
        if (!empty($lotNotes)) {
            $commentsFinal .= ' | Lotes solicitados: ' . implode('; ', $lotNotes);
        }
        $commentsFinal = substr($commentsFinal, 0, 254);

        $payload = [
            'DocDate'     => $today,
            'DueDate'     => $due,
            'TaxDate'     => $today,
            'FromWarehouse' => $from,
            'ToWarehouse'   => $to,
            'Comments'    => $commentsFinal,
            'JournalMemo' => 'Solicitud de traslado - WMS',
            'SalesPersonCode' => 2,
            // UDFs COMFAR (mismos que una ST válida del sistema)
            'U_CENT_DECLARABLE' => 'SI', 'U_CENT_SINI' => 'N', 'U_RETENCION' => 'N',
            'U_RETE_FACTURADA'  => 'N',  'U_CLAS_VENTA' => '01', 'U_imp_lote' => '01',
            'U_TIPOOPERACION'   => '2',  'U_concepto_nc_comp' => 'EP',
            'StockTransferLines' => $lines,
        ];

        try {
            $sl = new SapServiceLayerClient();
            $sl->login();
            $res = $sl->post('/InventoryTransferRequests', $payload);
            $docEntry = $res['DocEntry'] ?? null;
            $docNum   = $res['DocNum'] ?? null;

            // Registrar en bitácora de sincronización
            try {
                $db = Database::getConnection();
                $db->prepare(
                    "INSERT INTO sap_sync_log (sync_type, direction, sap_doc_entry, sap_doc_type, status, error_message)
                     VALUES ('TRANSFER_REQUEST', 'WMS_TO_SAP', ?, 'InventoryTransferRequest', 'SUCCESS', ?)"
                )->execute([$docEntry, "ST creada {$from}->{$to} DocNum={$docNum} lineas=" . count($lines)]);
            } catch (\Throwable $e) { /* log no debe romper la respuesta */ }

            Response::success([
                'docEntry' => $docEntry,
                'docNum'   => $docNum,
                'from'     => $from,
                'to'       => $to,
                'lines'    => count($lines),
            ], "Solicitud de Traslado creada en SAP (Nº {$docNum}).");
        } catch (\Throwable $e) {
            // 422 a propósito (NO 5xx): el gateway FortiGate reemplaza toda respuesta
            // 5xx por su propia página "error code: 502", ocultando el motivo real de SAP.
            // Con 4xx el JSON llega al navegador y el usuario ve el error de SAP.
            Response::error('No se pudo crear la Solicitud de Traslado en SAP: ' . $e->getMessage(), 422);
        }
    }

}
