<?php
namespace App\Controllers;
use App\Core\{View, Cart, Database as DB, Mailer};
use App\Models\{Event, Photo, Order, OrderItem};

class GalleryController
{
    public function home(): void
    {
        $events = Event::active();
        View::render('home', compact('events'), 'app');
    }

    public function contact(): void
    {
        View::render('contact', [], 'app');
    }

    public function show(string $slug): void
    {
        $event = Event::bySlug($slug);
        if (!$event) abort(404);

        // Verificar password se definida
        if (!empty($event['password'])) {
            $sessKey = 'event_unlocked_' . $event['id'];

            if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['event_password'])) {
                // Rate limiting na password do evento
                $ip       = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
                $rateKey  = 'event_pw_' . $event['id'] . '_' . $ip;

                if (!\App\Core\Security::rateLimit($rateKey, 5, 900)) {
                    View::render('event-password', ['event'=>$event,'error'=>true,'blocked'=>true], 'app');
                    return;
                }

                if (\password_verify($_POST['event_password'] ?? '', $event['password'])) {
                    \App\Core\Security::rateLimitClear($rateKey);
                    $_SESSION[$sessKey] = true;
                } else {
                    View::render('event-password', ['event'=>$event,'error'=>true,'blocked'=>false], 'app');
                    return;
                }
            }

            if (empty($_SESSION[$sessKey])) {
                View::render('event-password', ['event'=>$event,'error'=>false,'blocked'=>false], 'app');
                return;
            }
        }

        $page   = \max(1, (int)($_GET['p'] ?? 1));
        $pp     = 60;
        $photos = Photo::forEvent($event['id'], $page, $pp);
        $total  = Photo::countEvent($event['id']);
        $pages  = (int)\ceil($total / $pp);

        // Libertar session lock para não bloquear outros pedidos
        \session_write_close();

        View::render('gallery', compact('event','photos','page','pages','total'), 'app');
    }
}

class CartController
{
    public function show(): void
    {
        $cart   = Cart::get();
        $photos = [];
        if ($cart) {
            $rows = Photo::findMany(\array_keys($cart));
            foreach ($rows as $p) $photos[$p['id']] = $p;
        }
        $totals = Cart::totals();
        View::render('cart', compact('cart','photos','totals'), 'app');
    }

    public function add(): void
    {
        if (!$this->csrfOk()) { $this->json(); return; }
        $id  = (int)($_POST['photo_id'] ?? 0);
        $qty = \max(1, \min(99, (int)($_POST['qty'] ?? 1)));
        if ($id > 0) Cart::add($id, $qty);
        $this->json();
    }

    public function update(): void
    {
        if (!$this->csrfOk()) { $this->json(); return; }
        $id  = (int)($_POST['photo_id'] ?? 0);
        $qty = \max(0, \min(99, (int)($_POST['qty'] ?? 0)));
        if ($id > 0) Cart::set($id, $qty);
        $this->json();
    }

    public function remove(): void
    {
        if (!$this->csrfOk()) { $this->json(); return; }
        $id = (int)($_POST['photo_id'] ?? 0);
        if ($id > 0) Cart::remove($id);
        if (!empty($_SERVER['HTTP_X_REQUESTED_WITH'])) { $this->json(); return; }
        redirect(base_url('carrinho'));
    }

    public function clear(): void
    {
        if (csrf_ok()) Cart::clear();
        redirect(base_url('carrinho'));
    }

    private function csrfOk(): bool
    {
        $token = $_POST['_csrf'] ?? '';
        return \hash_equals(csrf_token(), $token);
    }

    private function json(): void
    {
        \header('Content-Type: application/json');
        echo \json_encode(['count' => Cart::count(), 'totals' => Cart::totals()]);
        exit;
    }
}

class CheckoutController
{
    private const MAX_FIELD = [
        'name'     => 150,
        'phone'    => 30,
        'email'    => 150,
        'nif'      => 20,
        'addr_rua' => 200,
        'addr_num' => 10,
        'addr_cp'  => 10,
        'addr_loc' => 100,
    ];

    public function show(): void
    {
        if (!Cart::count()) redirect(base_url());
        $totals = Cart::totals();
        $preco  = config('app.preco_foto');
        View::render('checkout', compact('totals','preco'), 'app');
    }

    public function process(): void
    {
        if (!csrf_ok() || !Cart::count()) redirect(base_url('carrinho'));

        // Validação e sanitização com limites de tamanho
        $data = [];
        foreach (self::MAX_FIELD as $field => $max) {
            $val = \trim($_POST[$field] ?? '');
            $data[$field] = \mb_substr($val, 0, $max);
        }

        $fieldErrors = [];
        if (empty($data['name'])) $fieldErrors[] = 'name';
        if ($data['email'] && !\filter_var($data['email'], FILTER_VALIDATE_EMAIL))
            $fieldErrors[] = 'email';

        if (!empty($fieldErrors)) {
            flash('error', 'Preenche os campos assinalados a vermelho.');
            flash('field_errors', $fieldErrors);
            redirect(base_url('checkout'));
        }

        // Validar NIF português se fornecido
        if ($data['nif'] && !\preg_match('/^\d{9}$/', $data['nif'])) {
            $data['nif'] = ''; // ignorar NIF inválido em vez de dar erro
        }

        // Validar shipping_method
        $validShipping = ['noivos', 'ctt', 'internacional'];
        $ship = $_POST['shipping_method'] ?? 'noivos';
        if (!\in_array($ship, $validShipping, true)) $ship = 'noivos';

        // Validar payment_method
        $validPayment = ['dinheiro', 'mbway'];
        $payMethod = $_POST['payment_method'] ?? 'dinheiro';
        if (!\in_array($payMethod, $validPayment, true)) $payMethod = 'dinheiro';

        if (\in_array($ship, ['ctt','internacional'])) {
            $addrErrors = [];
            if (empty($data['addr_rua'])) $addrErrors[] = 'addr_rua';
            if (empty($data['addr_cp']))  $addrErrors[] = 'addr_cp';
            if (empty($data['addr_loc'])) $addrErrors[] = 'addr_loc';
            if (!empty($addrErrors)) {
                flash('error', 'Preenche a morada completa.');
                flash('field_errors', array_merge($fieldErrors ?? [], $addrErrors));
                redirect(base_url('checkout'));
            }
        }

        // Montar morada
        $address = '';
        if (\in_array($ship, ['ctt','internacional'])) {
            $address = $data['addr_rua'] . ($data['addr_num'] ? ', '.$data['addr_num'] : '')
                     . "\n" . $data['addr_cp'] . ' ' . $data['addr_loc'];
        }

        $totals   = Cart::totals();
        $shipCost = Cart::shipping($ship);
        $total    = $totals['subtotal'] + $shipCost;
        $cart     = Cart::get();
        $photoIds = \array_keys($cart);

        $first = Photo::find((int)($photoIds[0] ?? 0));
        $event = $first ? Event::find($first['event_id']) : null;
        $orderNum = Order::nextNumber($event['order_prefix'] ?? 'ENC', $event['id'] ?? 0);

        $orderId = DB::tx(function() use (
            $cart,$totals,$shipCost,$total,$ship,$event,
            $orderNum,$photoIds,$address,$data
        ) {
            $oid = Order::create([
                'event_id'        => $event['id'] ?? null,
                'event_name'      => $event['name'] ?? '',
                'order_number'    => $orderNum,
                'customer_name'   => $data['name'],
                'phone'           => $data['phone'],
                'email'           => $data['email'],
                'nif'             => $data['nif'],
                'shipping_method'  => $ship,
                'payment_method'   => $payMethod,
                'address'         => $address,
                'subtotal'        => $totals['subtotal'],
                'shipping_cost'   => $shipCost,
                'total'           => $total,
                'photos_free'     => $totals['free'],
            ]);
            $up = config('app.preco_foto');
            foreach (Photo::findMany($photoIds) as $p) {
                $q = \min(99, (int)($cart[$p['id']] ?? 1));
                OrderItem::create([
                    'order_id'   => $oid,
                    'photo_id'   => $p['id'],
                    'filename'   => $p['filename'],
                    'quantity'   => $q,
                    'unit_price' => $up,
                    'subtotal'   => $q * $up,
                ]);
            }
            return $oid;
        });

        $order = Order::find($orderId);
        Mailer::orderNotification($order, Order::items($orderId));
        // Registar token de sessão ANTES do redirect para /obrigado/
        $orderId = DB::one("SELECT id FROM orders WHERE order_number=?", [$orderNum])['id'] ?? 0;
        if ($orderId) $_SESSION['last_order_' . $orderId] = true;

        Cart::clear();
        unset($_SESSION['_old']);
        redirect(base_url('obrigado/' . $orderNum));
    }

    public function thankyou(string $num): void
    {
        $order = Order::byNumber(\preg_replace('/[^A-Z0-9\-]/', '', \strtoupper($num)));
        if (!$order) abort(404);

        // Verificar se a encomenda pertence à sessão actual
        // Na primeira visita (logo após o checkout) a sessão tem o token
        $sessKey = 'last_order_' . $order['id'];
        if (empty($_SESSION[$sessKey])) {
            // Não é o comprador — não expor dados
            redirect(base_url());
        }

        $items = Order::items($order['id']);
        View::render('thankyou', compact('order','items'), 'app');
    }

    public function recibo(string $num): void
    {
        // Recibo só acessível com sessão de compra válida ou como admin
        $order = Order::byNumber(\preg_replace('/[^A-Z0-9\-]/', '', \strtoupper($num)));
        if (!$order) abort(404);

        // Só mostra se: foi compra desta sessão OU é admin
        $isAdmin    = !empty($_SESSION['admin']);
        $isOwnOrder = !empty($_SESSION['last_order_' . $order['id']]);

        if (!$isAdmin && !$isOwnOrder) abort(403);

        $items      = Order::items($order['id']);
        $totals_pay = $order['photos_free'] > 0
            ? (\array_sum(\array_column($items,'quantity')) - $order['photos_free'])
            : \array_sum(\array_column($items,'quantity'));
        View::render('recibo', compact('order','items','totals_pay'));
    }
}
