<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Repositories\PedidoRepository;
use App\Repositories\RepartidorRepository;
use App\Repositories\TiendaRepository;
use App\Repositories\UsuarioRepository;
use App\Services\PushService;
use App\Services\TrackingService;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class PedidoController
{
    private PedidoRepository $pedidos;
    private UsuarioRepository $usuarios;
    private TiendaRepository $tiendas;
    private RepartidorRepository $repartidores;
    private TrackingService $tracking;
    private PushService $push;

    public function __construct(
        PedidoRepository $pedidos,
        UsuarioRepository $usuarios,
        TiendaRepository $tiendas,
        RepartidorRepository $repartidores,
        TrackingService $tracking,
        PushService $push
    ) {
        $this->pedidos = $pedidos;
        $this->usuarios = $usuarios;
        $this->tiendas = $tiendas;
        $this->repartidores = $repartidores;
        $this->tracking = $tracking;
        $this->push = $push;
    }

    public function listar(Request $request, Response $response): Response
    {
        $filtros = $request->getQueryParams();

        return $this->json($response, $this->pedidos->listarPorFiltro($filtros));
    }

    public function ver(Request $request, Response $response, array $args): Response
    {
        $pedido = $this->pedidos->buscarPorId((int) $args['id']);

        if (!$pedido) {
            return $this->json($response, ['error' => 'Pedido no encontrado'], 404);
        }

        return $this->json($response, $pedido);
    }

    /**
     * Endpoint público (sin JWT) consumido por la web de seguimiento.
     * Requiere el token único del pedido en query string.
     */
    public function verPublico(Request $request, Response $response, array $args): Response
    {
        $token = (string) ($request->getQueryParams()['token'] ?? '');
        $pedido = $this->pedidos->buscarPorIdYToken((int) $args['id'], $token);

        if (!$pedido) {
            return $this->json($response, ['error' => 'Pedido no encontrado'], 404);
        }

        unset($pedido['token_publico']);

        return $this->json($response, $pedido);
    }

    public function crear(Request $request, Response $response): Response
    {
        $body = (array) $request->getParsedBody();

        $requeridos = ['tienda_id', 'cliente_nombre', 'direccion_origen', 'origen_lat', 'origen_lng',
            'direccion_destino', 'destino_lat', 'destino_lng', 'costo_envio'];
        foreach ($requeridos as $campo) {
            if (!isset($body[$campo]) || $body[$campo] === '') {
                return $this->json($response, ['error' => "El campo {$campo} es requerido"], 422);
            }
        }

        $id = $this->pedidos->crear([
            'tienda_id' => (int) $body['tienda_id'],
            'cliente_nombre' => (string) $body['cliente_nombre'],
            'cliente_telefono' => (string) ($body['cliente_telefono'] ?? ''),
            'direccion_origen' => (string) $body['direccion_origen'],
            'origen_lat' => (float) $body['origen_lat'],
            'origen_lng' => (float) $body['origen_lng'],
            'direccion_destino' => (string) $body['direccion_destino'],
            'destino_lat' => (float) $body['destino_lat'],
            'destino_lng' => (float) $body['destino_lng'],
            'costo_envio' => (float) $body['costo_envio'],
        ]);

        $pedido = $this->pedidos->buscarPorId($id);

        return $this->json($response, $pedido, 201);
    }

    public function asignarRepartidor(Request $request, Response $response, array $args): Response
    {
        $pedidoId = (int) $args['id'];
        $body = (array) $request->getParsedBody();
        $repartidorId = (int) ($body['repartidor_id'] ?? 0);

        if (!$repartidorId) {
            return $this->json($response, ['error' => 'repartidor_id es requerido'], 422);
        }

        $repartidor = $this->repartidores->buscarPorId($repartidorId);
        if (!$repartidor) {
            return $this->json($response, ['error' => 'Repartidor no encontrado'], 404);
        }

        $this->pedidos->asignarRepartidor($pedidoId, $repartidorId);

        $tokens = $this->usuarios->obtenerTokensPush((int) $repartidor['usuario_id']);
        $this->push->enviar($tokens, 'Nuevo pedido asignado', "Se te asignó el pedido #{$pedidoId}", [
            'pedido_id' => (string) $pedidoId,
            'tipo' => 'pedido_asignado',
        ]);

        return $this->json($response, $this->pedidos->buscarPorId($pedidoId));
    }

    public function cambiarEstado(Request $request, Response $response, array $args): Response
    {
        $pedidoId = (int) $args['id'];
        $body = (array) $request->getParsedBody();
        $estadoNuevo = (string) ($body['estado'] ?? '');

        if (!in_array($estadoNuevo, PedidoRepository::ESTADOS_VALIDOS, true)) {
            return $this->json($response, ['error' => 'Estado inválido'], 422);
        }

        $pedido = $this->pedidos->buscarPorId($pedidoId);
        if (!$pedido) {
            return $this->json($response, ['error' => 'Pedido no encontrado'], 404);
        }

        $estadoAnterior = $pedido['estado'];
        $this->pedidos->cambiarEstado($pedidoId, $estadoNuevo);
        $this->tracking->emitirCambioEstado($pedidoId, $estadoAnterior, $estadoNuevo);

        $tienda = $this->tiendas->buscarPorId((int) $pedido['tienda_id']);
        if ($tienda && $tienda['usuario_id']) {
            $tokens = $this->usuarios->obtenerTokensPush((int) $tienda['usuario_id']);
            $this->push->enviar($tokens, 'Actualización de pedido', "El pedido #{$pedidoId} cambió a: {$estadoNuevo}", [
                'pedido_id' => (string) $pedidoId,
                'tipo' => 'cambio_estado',
                'estado' => $estadoNuevo,
            ]);
        }

        return $this->json($response, $this->pedidos->buscarPorId($pedidoId));
    }

    private function json(Response $response, $data, int $status = 200): Response
    {
        $response->getBody()->write(json_encode($data));

        return $response->withHeader('Content-Type', 'application/json')->withStatus($status);
    }
}
