<?php
namespace Modules\Compras\Controllers;

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

class PurchaseInvoicesController {
    public function list(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $page = max(1, (int)($r->query['page'] ?? 1));
        $per = min(100, max(1, (int)($r->query['per_page'] ?? 20)));
        $offset = ($page - 1) * $per;
        $stmt = $pdo->query("SELECT id, numero, fornecedor_id, valor, data_emissao, created_at FROM purchase_invoices ORDER BY id DESC LIMIT $per OFFSET $offset");
        $rows = $stmt->fetchAll();
        $total = (int)$pdo->query("SELECT COUNT(*) AS c FROM purchase_invoices")->fetch()['c'];
        AuditLogger::logRead('purchase_invoices', null, ['endpoint' => 'compras.notas.list', 'page' => $page, 'per_page' => $per]);
        return Response::json(['data' => $rows, 'page' => $page, 'per_page' => $per, 'total' => $total, 'total_pages' => (int)ceil($total / $per)]);
    }

    public function getById(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $id = (int)(($r->get('params', [])[0]) ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['code' => 'invalid_id', 'message' => 'ID inválido']]);
        $stmt = $pdo->prepare('SELECT id, numero, fornecedor_id, valor, data_emissao, created_at FROM purchase_invoices WHERE id = ? LIMIT 1');
        $stmt->execute([$id]);
        $row = $stmt->fetch();
        if (!$row) return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Nota não encontrada']]);
        $items = [];
        try {
            $it = $pdo->prepare('SELECT product_id, quantidade, preco, total FROM purchase_invoice_items WHERE invoice_id = ? ORDER BY id ASC');
            $it->execute([$id]);
            $items = $it->fetchAll();
        } catch (\Throwable $e) {
            // Sem itens persistidos ainda
        }
        AuditLogger::logRead('purchase_invoices', $id, ['endpoint' => 'compras.notas.get_by_id']);
        return Response::json(['data' => ['invoice' => $row, 'items' => $items]]);
    }

    public function create(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $data = $r->body;
        $numero = (string)($data['numero'] ?? '');
        $fornecedorId = (int)($data['fornecedor_id'] ?? 0);
        $valor = (float)($data['valor'] ?? 0);
        $dataEmissao = $data['data_emissao'] ?? null;
        $items = is_array($data['items'] ?? null) ? $data['items'] : [];
        if ($numero === '') return new Response(422, ['error' => ['code' => 'numero_required', 'message' => 'Número é obrigatório']]);
        if ($fornecedorId <= 0) return new Response(422, ['error' => ['code' => 'fornecedor_required', 'message' => 'fornecedor_id é obrigatório']]);
        // Validar fornecedor
        $fst = $pdo->prepare('SELECT id FROM suppliers WHERE id = ? LIMIT 1');
        $fst->execute([$fornecedorId]);
        if (!$fst->fetch()) return new Response(404, ['error' => ['code' => 'supplier_not_found', 'message' => 'Fornecedor não encontrado']]);
        // Se itens foram enviados, calcular o valor somando itens
        $summary = [];
        if (count($items) > 0) {
            $valor = 0.0;
            foreach ($items as $it) {
                $pid = (int)($it['product_id'] ?? 0);
                $qtd = (float)($it['quantidade'] ?? 0);
                $preco = (float)($it['preco'] ?? 0);
                if ($pid <= 0 || $qtd <= 0 || $preco <= 0) {
                    return new Response(422, ['error' => ['code' => 'invalid_item', 'message' => 'Item inválido: product_id, quantidade e preco > 0 requeridos']]);
                }
                // Validar produto existe
                $pst = $pdo->prepare('SELECT id FROM products WHERE id = ? LIMIT 1');
                $pst->execute([$pid]);
                if (!$pst->fetch()) return new Response(404, ['error' => 'Produto não encontrado']);
                $linhaTotal = $qtd * $preco;
                $valor += $linhaTotal;
                $summary[] = ['product_id' => $pid, 'quantidade' => $qtd, 'preco' => $preco, 'total' => $linhaTotal];
            }
        } else {
            if ($valor <= 0) return new Response(422, ['error' => ['code' => 'invalid_valor', 'message' => 'valor deve ser > 0']]);
        }
        $ins = $pdo->prepare('INSERT INTO purchase_invoices (numero, fornecedor_id, valor, data_emissao) VALUES (?,?,?,?)');
        $ins->execute([$numero, $fornecedorId, $valor, $dataEmissao]);
        $newId = (int)$pdo->lastInsertId();
        // Persistir itens e movimentar estoque (entrada)
        if (count($summary) > 0) {
            try {
                $itIns = $pdo->prepare('INSERT INTO purchase_invoice_items (invoice_id, product_id, quantidade, preco, total) VALUES (?,?,?,?,?)');
                foreach ($summary as $s) {
                    $itIns->execute([$newId, (int)$s['product_id'], (float)$s['quantidade'], (float)$s['preco'], (float)$s['total']]);
                }
            } catch (\Throwable $e) {
                // Ignorar se tabela ainda não existir
            }
            // Movimentar estoque: atualizar products e registrar motivo/estoque antes/depois, com referência da nota
            $movIns = $pdo->prepare('INSERT INTO inventory_movements (product_id, tipo, quantidade, motivo, stock_before, stock_after, origin_type, origin_id, origin_number) VALUES (?,?,?,?,?,?,?,?,?)');
            $pGet = $pdo->prepare('SELECT id, estoque FROM products WHERE id = ? LIMIT 1');
            $pUpd = $pdo->prepare('UPDATE products SET estoque = :estoque WHERE id = :id');
            foreach ($summary as $s) {
                $pid = (int)$s['product_id'];
                $qtd = (float)$s['quantidade'];
                $pGet->execute([$pid]);
                $prod = $pGet->fetch();
                $before = (float)($prod['estoque'] ?? 0);
                $after = $before + $qtd;
                $pUpd->execute([':estoque' => $after, ':id' => $pid]);
                $movIns->execute([$pid, 'entrada_compra', $qtd, 'Entrada de Nota', $before, $after, 'purchase_invoice', $newId, $numero]);
            }
        }
        AuditLogger::logCreate('purchase_invoices', $newId, ['numero' => $numero, 'fornecedor_id' => $fornecedorId, 'valor' => $valor, 'data_emissao' => $dataEmissao]);
        return Response::json(['ok' => true, 'id' => $newId], 201);
    }

    public function update(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $id = (int)(($r->get('params', [])[0]) ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['code' => 'invalid_id', 'message' => 'ID inválido']]);
        $data = $r->body;
        $numero = $data['numero'] ?? null;
        $fornecedorId = isset($data['fornecedor_id']) ? (int)$data['fornecedor_id'] : null;
        $valor = isset($data['valor']) ? (float)$data['valor'] : null;
        $dataEmissao = $data['data_emissao'] ?? null;
        $beforeStmt = $pdo->prepare('SELECT id, numero, fornecedor_id, valor, data_emissao FROM purchase_invoices WHERE id = ? LIMIT 1');
        $beforeStmt->execute([$id]);
        $before = $beforeStmt->fetch();
        if (!$before) return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Nota não encontrada']]);
        $stmt = $pdo->prepare('UPDATE purchase_invoices SET numero = COALESCE(?, numero), fornecedor_id = COALESCE(?, fornecedor_id), valor = COALESCE(?, valor), data_emissao = COALESCE(?, data_emissao) WHERE id = ?');
        $stmt->execute([$numero, $fornecedorId, $valor, $dataEmissao, $id]);
        $after = $before;
        if ($numero !== null) $after['numero'] = $numero;
        if ($fornecedorId !== null) $after['fornecedor_id'] = $fornecedorId;
        if ($valor !== null) $after['valor'] = $valor;
        if ($dataEmissao !== null) $after['data_emissao'] = $dataEmissao;
        AuditLogger::logUpdate('purchase_invoices', $id, $before ?: [], $after ?: [], ['endpoint' => 'compras.notas.update']);
        return Response::json(['ok' => true]);
    }

    public function delete(Request $r): Response {
        $pdo = ConnectionManager::forTenant();
        $id = (int)(($r->get('params', [])[0]) ?? 0);
        if ($id <= 0) return new Response(422, ['error' => ['code' => 'invalid_id', 'message' => 'ID inválido']]);
        $beforeStmt = $pdo->prepare('SELECT id, numero, fornecedor_id, valor, data_emissao FROM purchase_invoices WHERE id = ? LIMIT 1');
        $beforeStmt->execute([$id]);
        $before = $beforeStmt->fetch();
        if (!$before) return new Response(404, ['error' => ['code' => 'not_found', 'message' => 'Nota não encontrada']]);
        $stmt = $pdo->prepare('DELETE FROM purchase_invoices WHERE id = ?');
        $stmt->execute([$id]);
        AuditLogger::logDelete('purchase_invoices', $id, $before ?: [], ['endpoint' => 'compras.notas.delete']);
        return Response::json(['ok' => true]);
    }
}