<?php

namespace WMS\Controllers;

use WMS\Core\Request;
use WMS\Core\Response;
use WMS\Middleware\AuthMiddleware;
use WMS\Services\AgendaService;
use WMS\Repositories\AgendaRepository;
use WMS\Helpers\Validator;

/**
 * Controlador de Agenda de Recepcion.
 *
 * Endpoints:
 * - POST   /api/agendas                       -> create
 * - GET    /api/agendas                        -> index
 * - GET    /api/agendas/{id}                   -> show
 * - POST   /api/agendas/{id}/references        -> addReference
 * - DELETE /api/agendas/{id}/references/{ref_id} -> removeReference
 * - POST   /api/agendas/{id}/open              -> open
 * - POST   /api/agendas/{id}/scan              -> scan
 * - GET    /api/agendas/{id}/differences       -> getDifferences
 * - POST   /api/agendas/{id}/close             -> close
 */
class AgendaController
{
    /**
     * POST /api/agendas
     * Crea una nueva agenda de recepcion.
     * Body: { warehouse_id, planned_date, notes? }
     */
    public static function create(array $params): void
    {
        $body = Request::body();
        $v = (new Validator($body))
            ->required(['warehouse_id', 'planned_date'])
            ->date('planned_date');

        if (!$v->passes()) {
            Response::error('Datos invalidos', 422, $v->errors());
            return;
        }

        try {
            $service = new AgendaService();
            $result = $service->createAgenda($body, AuthMiddleware::userId());
            Response::success($result, 'Agenda creada', 201);
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * GET /api/agendas
     * Lista agendas filtradas por warehouse_id y opcionalmente por status.
     */
    public static function index(array $params): void
    {
        $warehouseId = Request::query('warehouse_id');
        if (!$warehouseId) {
            Response::error('El parametro warehouse_id es obligatorio', 422);
            return;
        }

        try {
            $repo = new AgendaRepository();
            $status = Request::query('status');
            $agendas = $repo->getByWarehouse((int) $warehouseId, $status);
            Response::success($agendas);
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * GET /api/agendas/{id}
     * Retorna una agenda con sus referencias, lineas, escaneos y diferencias.
     */
    public static function show(array $params): void
    {
        try {
            $repo = new AgendaRepository();
            $agenda = $repo->getWithDetails((int) $params['id']);
            if (!$agenda) {
                Response::error('Agenda no encontrada', 404);
                return;
            }
            Response::success($agenda);
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * POST /api/agendas/{id}/references
     * Asocia una OC de SAP a la agenda.
     * Body: { sap_doc_entry }
     */
    public static function addReference(array $params): void
    {
        $body = Request::body();
        if (empty($body['sap_doc_entry']) && empty($body['sap_doc_num'])) {
            Response::error('Ingrese el Nro. de OC (DocNum) o DocEntry', 400);
            return;
        }

        try {
            $service = new AgendaService();
            $result = $service->addReference(
                (int) $params['id'],
                $body,
                AuthMiddleware::userId()
            );
            // El mensaje refleja el tipo real (OC vs ST) que se asocio.
            $refs = $result['references'] ?? [];
            $lastRef = end($refs) ?: null;
            $lastType = $lastRef['sap_doc_type'] ?? 'PurchaseOrder';
            $short = ($lastType === 'InventoryTransferRequest') ? 'ST' : 'OC';
            Response::success($result, $short . ' asociada a la agenda', 201);
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * DELETE /api/agendas/{id}/references/{ref_id}
     * Elimina una referencia (OC) de la agenda.
     */
    public static function removeReference(array $params): void
    {
        try {
            $service = new AgendaService();
            $result = $service->removeReference(
                (int) $params['id'],
                (int) $params['ref_id'],
                AuthMiddleware::userId()
            );
            Response::success($result, 'Referencia eliminada');
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * POST /api/agendas/import-sap-po
     * Crea agenda + asocia OC SAP + auto-asigna al usuario si rol = op_recepcion.
     * Body: { sap_doc_entry?, sap_doc_num?, warehouse_id, planned_date?, notes? }
     */
    public static function importSapPo(array $params): void
    {
        $body = Request::body();
        try {
            $service = new AgendaService();
            $u = AuthMiddleware::user();
            $result = $service->importFromSapPurchaseOrder(
                $body,
                AuthMiddleware::userId(),
                $u['role'] ?? null
            );
            Response::success($result, 'Agenda creada desde OC SAP');
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * POST /api/agendas/import-sap-tr
     * Crea agenda + asocia Solicitud de Traslado (OWTQ) SAP.
     * Body: { sap_doc_entry?, sap_doc_num?, warehouse_id, planned_date?, notes? }
     */
    public static function importSapTransferRequest(array $params): void
    {
        $body = Request::body();
        try {
            $service = new AgendaService();
            $u = AuthMiddleware::user();
            $result = $service->importFromSapStockTransferRequest(
                $body,
                AuthMiddleware::userId(),
                $u['role'] ?? null
            );
            Response::success($result, 'Agenda creada desde Solicitud de Traslado SAP');
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * POST /api/agendas/{id}/reconcile-stock
     * Genera stock para una agenda CERRADA cuyos escaneos no llegaron al WMS.
     */
    public static function reconcileStock(array $params): void
    {
        try {
            $service = new AgendaService();
            $result = $service->reconcileStock(
                (int) $params['id'],
                AuthMiddleware::userId()
            );
            Response::success($result, 'Stock reconciliado');
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * POST /api/agenda-refs/{id}/retry-sap
     * Reintenta crear el PurchaseDeliveryNote en SAP para una ref en ERROR.
     */
    public static function retrySap(array $params): void
    {
        try {
            $service = new AgendaService();
            $result = $service->retrySapForRef(
                (int) $params['id'],
                AuthMiddleware::userId()
            );
            Response::success($result, 'Documento creado en SAP');
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * POST /api/agendas/{id}/assign
     * Asigna la agenda a un operador. Body: { user_id, notes? }
     */
    public static function assign(array $params): void
    {
        $body = Request::body();
        if (empty($body['user_id'])) { Response::error('user_id requerido'); return; }

        try {
            $service = new AgendaService();
            $result = $service->assignAgenda(
                (int) $params['id'],
                (int) $body['user_id'],
                AuthMiddleware::userId(),
                $body['notes'] ?? null
            );
            Response::success($result, 'Agenda asignada');
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * POST /api/agendas/{id}/open
     * Abre la agenda para recepcion fisica.
     */
    public static function open(array $params): void
    {
        try {
            $service = new AgendaService();
            $result = $service->openAgenda(
                (int) $params['id'],
                AuthMiddleware::userId()
            );
            Response::success($result, 'Agenda abierta para recepcion');
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * POST /api/agendas/{id}/scan
     * Registra un escaneo ciego de producto.
     * Body: { item_code, quantity, batch_number?, expiry_date?, supplier_lot?, bin_id? }
     */
    public static function scan(array $params): void
    {
        $body = Request::body();
        $v = (new Validator($body))
            ->required(['item_code', 'quantity'])
            ->positiveNumber('quantity');

        if (!$v->passes()) {
            Response::error('Datos invalidos', 422, $v->errors());
            return;
        }

        try {
            $service = new AgendaService();
            $result = $service->scanItem(
                (int) $params['id'],
                $body,
                AuthMiddleware::userId()
            );
            Response::success($result, 'Escaneo registrado');
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * GET /api/agendas/{id}/differences
     * Compara lo esperado vs lo recibido.
     */
    public static function getDifferences(array $params): void
    {
        try {
            $service = new AgendaService();
            $result = $service->getDifferences((int) $params['id']);
            Response::success($result);
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * GET /api/agendas/with-diffs
     * Lista refs con recepcion PARCIAL (qty recibida < esperada) para alertar a Compras.
     * Query params:
     *   - warehouse_id (opcional)
     *   - days  (opcional, default 30)
     */
    public static function withDiffs(array $params): void
    {
        try {
            $whId = Request::query('warehouse_id');
            $days = (int)(Request::query('days') ?: 30);
            $db = \WMS\Core\Database::getConnection();
            $sql = "SELECT a.id AS agenda_id, a.agenda_number, a.warehouse_id, w.name AS warehouse_name,
                           a.status AS agenda_status, a.closed_at,
                           r.id AS ref_id, r.sap_doc_entry, r.sap_doc_num, r.supplier_code, r.supplier_name,
                           r.status AS ref_status,
                           SUM(l.expected_qty) AS qty_expected,
                           SUM(l.received_qty) AS qty_received,
                           SUM(GREATEST(l.expected_qty - l.received_qty, 0)) AS qty_short,
                           SUM(CASE WHEN l.received_qty < l.expected_qty THEN 1 ELSE 0 END) AS lines_short,
                           COUNT(*) AS lines_total
                    FROM reception_agenda_refs r
                    JOIN reception_agendas a ON a.id = r.agenda_id
                    LEFT JOIN warehouses w ON w.id = a.warehouse_id
                    JOIN reception_agenda_lines l ON l.ref_id = r.id
                    WHERE r.status = 'PARTIAL'
                      AND a.created_at >= NOW() - INTERVAL ? DAY";
            $args = [$days];
            if ($whId) { $sql .= " AND a.warehouse_id = ?"; $args[] = (int)$whId; }
            $sql .= " GROUP BY r.id ORDER BY a.closed_at DESC, a.id DESC LIMIT 200";
            $stmt = $db->prepare($sql);
            $stmt->execute($args);
            Response::success($stmt->fetchAll(\PDO::FETCH_ASSOC));
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * GET /api/agendas/{id}/acta.pdf
     * Devuelve el PDF del acta de recepción. Si no existe, lo regenera.
     */
    public static function actaPdf(array $params): void
    {
        try {
            $agendaId = (int)$params['id'];
            $svc = new \WMS\Services\ReceptionPdfService();
            $path = $svc->generate($agendaId, AuthMiddleware::userId());
            if (!file_exists($path)) { Response::error('PDF no generado', 500); return; }
            header('Content-Type: application/pdf');
            header('Content-Disposition: inline; filename="' . basename($path) . '"');
            header('Content-Length: ' . filesize($path));
            readfile($path);
            exit;
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }

    /**
     * POST /api/agendas/{id}/close
     * Cierra la agenda y crea PurchaseDeliveryNotes en SAP.
     */
    public static function close(array $params): void
    {
        try {
            $service = new AgendaService();
            $result = $service->closeAgenda(
                (int) $params['id'],
                AuthMiddleware::userId()
            );
            Response::success($result, 'Agenda cerrada');
        } catch (\RuntimeException $e) {
            Response::error($e->getMessage());
        }
    }
}
