<?php
namespace Modules\Wms\Controllers;

use Gestoreasy\Core\Http\Request;
use Gestoreasy\Core\Http\Response;
use Gestoreasy\Core\Tenant\ConnectionManager;
use Gestoreasy\Core\Audit\AuditLogger;

class InventoryController {
    private function pdo(): \PDO { return ConnectionManager::forTenant(); }

    private function ensureTables(\PDO $pdo): void {
        // Sessões de inventário
        try {
            $pdo->exec('CREATE TABLE IF NOT EXISTS wms_inventory_sessions (
                id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                tipo ENUM("ciclico","geral") NOT NULL DEFAULT "geral",
                status ENUM("aberto","em_andamento","concluido","cancelado") NOT NULL DEFAULT "aberto",
                entries_count INT UNSIGNED NOT NULL DEFAULT 0,
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
                updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                INDEX idx_wms_inv_status (status)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
        } catch (\Throwable $e) {}

        // Configurações do WMS
        try {
            $pdo->exec('CREATE TABLE IF NOT EXISTS wms_settings (
                id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                policy ENUM("fifo","fefo","lifo") NOT NULL DEFAULT "fifo",
                enable_rfid TINYINT(1) NOT NULL DEFAULT 0,
                pick_strategy ENUM("nearest_bin","zone_batch","wave") NOT NULL DEFAULT "nearest_bin",
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
                updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4');
        } catch (\Throwable $e) {}
    }

    public function listSessions(Request $r): Response {
        $pdo = $this->pdo();
        $this->ensureTables($pdo);
        try {
            $stmt = $pdo->query('SELECT id, tipo AS type, status, updated_at FROM wms_inventory_sessions ORDER BY updated_at DESC, id DESC');
            $rows = $stmt->fetchAll();
            return Response::json(['data' => array_map(function($row){
                return [
                    'id' => (int)$row['id'],
                    'type' => (string)$row['type'],
                    'status' => (string)$row['status'],
                    'updated_at' => (string)$row['updated_at']
                ];
            }, $rows)]);
        } catch (\Throwable $e) {
            return new Response(500, ['error' => 'Falha ao listar inventários']);
        }
    }

    public function startSession(Request $r): Response {
        $pdo = $this->pdo();
        $this->ensureTables($pdo);
        $type = (string)($r->body['type'] ?? 'geral');
        $entries = (int)($r->body['entries_count'] ?? 0);
        if (!in_array($type, ['geral','ciclico'], true)) { return new Response(422, ['error' => 'type inválido']); }
        if ($entries <= 0) { return new Response(422, ['error' => 'entries_count deve ser > 0']); }
        try {
            $stmt = $pdo->prepare('INSERT INTO wms_inventory_sessions (tipo, status, entries_count) VALUES (?,?,?)');
            $stmt->execute([$type, 'em_andamento', $entries]);
            $id = (int)$pdo->lastInsertId();
            AuditLogger::log('update', 'wms.inventory', $id, null, ['tipo' => $type, 'status' => 'em_andamento', 'entries_count' => $entries], ['endpoint' => 'wms.inventory.start']);
            return Response::json(['ok' => true, 'created' => [ 'id' => $id, 'type' => $type, 'status' => 'em_andamento' ]]);
        } catch (\Throwable $e) {
            return new Response(500, ['error' => 'Falha ao iniciar inventário']);
        }
    }

    public function getSettings(Request $r): Response {
        $pdo = $this->pdo();
        $this->ensureTables($pdo);
        try {
            $stmt = $pdo->query('SELECT policy, enable_rfid, pick_strategy, updated_at FROM wms_settings ORDER BY id DESC LIMIT 1');
            $row = $stmt->fetch();
            $settings = [
                'policy' => (string)($row['policy'] ?? 'fifo'),
                'enableRfid' => (bool)($row['enable_rfid'] ?? false),
                'pick_strategy' => (string)($row['pick_strategy'] ?? 'nearest_bin'),
                'updated_at' => (string)($row['updated_at'] ?? '')
            ];
            return Response::json(['settings' => $settings]);
        } catch (\Throwable $e) {
            return new Response(500, ['error' => 'Falha ao consultar configurações do WMS']);
        }
    }

    public function saveSettings(Request $r): Response {
        $pdo = $this->pdo();
        $this->ensureTables($pdo);

        $policy = (string)($r->body['policy'] ?? 'fifo');
        $enableRfid = (bool)($r->body['enableRfid'] ?? false);
        $pick = (string)($r->body['pick_strategy'] ?? 'nearest_bin');

        if (!in_array($policy, ['fifo','fefo','lifo'], true)) { return new Response(422, ['error' => 'policy inválida']); }
        if (!in_array($pick, ['nearest_bin','zone_batch','wave'], true)) { return new Response(422, ['error' => 'pick_strategy inválida']); }

        try {
            // Obter anterior para auditoria
            $prevStmt = $pdo->query('SELECT policy, enable_rfid, pick_strategy FROM wms_settings ORDER BY id DESC LIMIT 1');
            $prev = $prevStmt->fetch() ?: null;

            $stmt = $pdo->prepare('INSERT INTO wms_settings (policy, enable_rfid, pick_strategy) VALUES (?,?,?)');
            $stmt->execute([$policy, $enableRfid ? 1 : 0, $pick]);
            AuditLogger::log('update', 'wms.settings', null, $prev ? [
                'policy' => (string)$prev['policy'],
                'enableRfid' => (bool)$prev['enable_rfid'],
                'pick_strategy' => (string)$prev['pick_strategy']
            ] : null, [
                'policy' => $policy,
                'enableRfid' => $enableRfid,
                'pick_strategy' => $pick
            ], ['endpoint' => 'wms.settings.save']);
            return Response::json(['ok' => true]);
        } catch (\Throwable $e) {
            return new Response(500, ['error' => 'Falha ao salvar configurações do WMS']);
        }
    }

    public function listEntries(Request $r): Response {
        $pdo = $this->pdo();
        $this->ensureTables($pdo);
        $page = max(1, (int)($r->query['page'] ?? 1));
        $per = min(200, max(1, (int)($r->query['per_page'] ?? 50)));
        $offset = ($page - 1) * $per;
        $sessionId = (int)($r->query['session_id'] ?? 0);
        $q = trim((string)($r->query['q'] ?? ''));

        // Garantir tabela de entries (se outra parte do sistema ainda não criou)
        try {
            $pdo->exec("CREATE TABLE IF NOT EXISTS wms_inventory_entries (
                id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
                session_id BIGINT UNSIGNED NULL,
                product_id BIGINT UNSIGNED NULL,
                bin_id BIGINT UNSIGNED NULL,
                quantity DECIMAL(12,3) NOT NULL,
                created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
                INDEX idx_entries_session (session_id),
                INDEX idx_entries_product (product_id)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
        } catch (\Throwable $_) {}

        $where = [];
        $params = [];
        if ($sessionId > 0) { $where[] = 'e.session_id = :sid'; $params[':sid'] = $sessionId; }
        if ($q !== '') { $where[] = '(p.codigo LIKE :q OR b.code LIKE :q)'; $params[':q'] = '%' . $q . '%'; }
        $wsql = $where ? ('WHERE ' . implode(' AND ', $where)) : '';
        $sql = "SELECT e.id, e.session_id, e.quantity, e.created_at,
                       p.codigo AS product_code, p.descricao AS product_name,
                       b.code AS bin_code
                FROM wms_inventory_entries e
                LEFT JOIN products p ON p.id = e.product_id
                LEFT JOIN bins b ON b.id = e.bin_id
                $wsql
                ORDER BY e.created_at DESC, e.id DESC
                LIMIT $per OFFSET $offset";
        $stmt = $pdo->prepare($sql);
        foreach ($params as $k => $v) { $stmt->bindValue($k, $v); }
        $stmt->execute();
        $rows = $stmt->fetchAll();

        $countSql = "SELECT COUNT(*) AS c FROM wms_inventory_entries e
                     LEFT JOIN products p ON p.id = e.product_id
                     LEFT JOIN bins b ON b.id = e.bin_id $wsql";
        $cStmt = $pdo->prepare($countSql);
        foreach ($params as $k => $v) { $cStmt->bindValue($k, $v); }
        $cStmt->execute();
        $total = (int)($cStmt->fetch()['c'] ?? 0);

        AuditLogger::logRead('wms_inventory_entries', null, ['endpoint' => 'wms.inventory.entries.list', 'page' => $page, 'per_page' => $per, 'session_id' => $sessionId, 'q' => $q]);
        return Response::json(['data' => $rows, 'page' => $page, 'per_page' => $per, 'total' => $total, 'total_pages' => (int)ceil($total / $per)]);
    }

    public function exportCsvEntries(Request $r): Response {
        $pdo = $this->pdo();
        $this->ensureTables($pdo);
        $sep = (string)($r->query['sep'] ?? ';');
        if (!in_array($sep, [';', ',', '\t'], true)) { $sep = ';'; }
        if ($sep === '\t') { $sep = "\t"; }
        $sessionId = (int)($r->query['session_id'] ?? 0);
        $q = trim((string)($r->query['q'] ?? ''));

        // Garantir tabela
        try { $pdo->exec('CREATE TABLE IF NOT EXISTS wms_inventory_entries (id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, session_id BIGINT UNSIGNED NULL, product_id BIGINT UNSIGNED NULL, bin_id BIGINT UNSIGNED NULL, quantity DECIMAL(12,3) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4'); } catch (\Throwable $_) {}

        $where = [];
        $params = [];
        if ($sessionId > 0) { $where[] = 'e.session_id = :sid'; $params[':sid'] = $sessionId; }
        if ($q !== '') { $where[] = '(p.codigo LIKE :q OR b.code LIKE :q)'; $params[':q'] = '%' . $q . '%'; }
        $wsql = $where ? ('WHERE ' . implode(' AND ', $where)) : '';
        $sql = "SELECT e.session_id, p.codigo AS product_code, b.code AS bin_code, e.quantity, e.created_at
                FROM wms_inventory_entries e
                LEFT JOIN products p ON p.id = e.product_id
                LEFT JOIN bins b ON b.id = e.bin_id $wsql
                ORDER BY e.created_at DESC, e.id DESC";
        $stmt = $pdo->prepare($sql);
        foreach ($params as $k => $v) { $stmt->bindValue($k, $v); }
        $stmt->execute();
        $rows = $stmt->fetchAll();

        $bom = chr(0xEF) . chr(0xBB) . chr(0xBF);
        $out = fopen('php://temp', 'r+');
        fputs($out, $bom);
        fputcsv($out, ['Sessão','Produto','Bin','Quantidade','Criado em'], $sep);
        foreach ($rows as $rRow) {
            fputcsv($out, [
                (string)($rRow['session_id'] ?? ''),
                (string)($rRow['product_code'] ?? ''),
                (string)($rRow['bin_code'] ?? ''),
                (string)($rRow['quantity'] ?? ''),
                (string)($rRow['created_at'] ?? ''),
            ], $sep);
        }
        rewind($out);
        $csv = stream_get_contents($out);
        fclose($out);

        AuditLogger::logRead('wms_inventory_entries', null, ['endpoint' => 'wms.inventory.entries.export.csv', 'rows' => count($rows), 'session_id' => $sessionId, 'q' => $q]);
        return new Response(200, $csv, ['Content-Type' => 'text/csv; charset=utf-8']);
    }
}