<?php

class TrackingController extends Controller
{
    public function index(): void
    {
        Auth::requireLogin();
        if (!can('tracking.view')) {
            http_response_code(403);
            require app_path('Views/errors/403.php');
            exit;
        }
        $this->view('tracking/index');
    }

    public function data(): void
    {
        Auth::requireLogin();
        if (!can('tracking.view')) json_response(['ok'=>false], 403);
        $db = Database::connection();
        $rows = $db->query("SELECT l.id load_id,l.load_number,l.status,l.current_latitude latitude,l.current_longitude longitude,l.last_location_address,l.last_location_at,d.name driver_name,t.unit_number,tr.trailer_number,ts.status tracking_status,ts.last_ping_at FROM loads l JOIN drivers d ON d.id=l.driver_id LEFT JOIN trucks t ON t.id=l.truck_id LEFT JOIN trailers tr ON tr.id=l.trailer_id LEFT JOIN tracking_sessions ts ON ts.id=(SELECT id FROM tracking_sessions x WHERE x.load_id=l.id ORDER BY x.id DESC LIMIT 1) WHERE l.status NOT IN ('paid','cancelled') AND l.current_latitude IS NOT NULL ORDER BY l.last_location_at DESC")->fetchAll();
        json_response(['ok'=>true, 'locations'=>$rows, 'generated_at'=>date('c')]);
    }

    public function ping(): void
    {
        Auth::requireRole(['driver']);
        Csrf::verify();
        $user = Auth::user();
        $loadId = (int) ($_POST['load_id'] ?? 0);
        $db = Database::connection();
        $stmt = $db->prepare('SELECT id FROM loads WHERE id=? AND driver_id=?');
        $stmt->execute([$loadId, (int) ($user['driver_id'] ?? 0)]);
        if (!$stmt->fetchColumn()) json_response(['ok'=>false, 'message'=>'Load not available'], 403);

        $latitude = filter_var($_POST['latitude'] ?? null, FILTER_VALIDATE_FLOAT);
        $longitude = filter_var($_POST['longitude'] ?? null, FILTER_VALIDATE_FLOAT);
        if ($latitude === false || $longitude === false) json_response(['ok'=>false, 'message'=>'Invalid location'], 422);

        $sessionId = (int) ($_POST['session_id'] ?? 0);
        if (!$sessionId) {
            $stmt = $db->prepare("INSERT INTO tracking_sessions (driver_id,user_id,load_id,status,started_at,last_ping_at,consent_at) VALUES (?,?,?,'active',NOW(),NOW(),NOW())");
            $stmt->execute([(int)$user['driver_id'], (int)$user['id'], $loadId]);
            $sessionId = (int) $db->lastInsertId();
        } else {
            $db->prepare("UPDATE tracking_sessions SET status='active',last_ping_at=NOW() WHERE id=? AND driver_id=?")->execute([$sessionId, (int)$user['driver_id']]);
        }

        (new Load())->updateLocation(
            $loadId,
            (int) $user['driver_id'],
            (float) $latitude,
            (float) $longitude,
            $this->number($_POST['accuracy'] ?? null),
            null,
            'live_tracking',
            null,
            $this->number($_POST['speed'] ?? null),
            $this->number($_POST['heading'] ?? null),
            $this->number($_POST['battery_level'] ?? null)
        );

        $geofence = $this->processBorderGeofence($loadId, (float)$latitude, (float)$longitude);
        json_response(['ok'=>true, 'session_id'=>$sessionId, 'received_at'=>date('c'), 'border_geofence'=>$geofence]);
    }

    public function stop(): void
    {
        Auth::requireRole(['driver']);
        Csrf::verify();
        $user = Auth::user();
        Database::connection()->prepare("UPDATE tracking_sessions SET status='stopped',stopped_at=NOW() WHERE id=? AND driver_id=?")
            ->execute([(int)($_POST['session_id'] ?? 0), (int)($user['driver_id'] ?? 0)]);
        json_response(['ok'=>true]);
    }

    private function processBorderGeofence(int $loadId, float $latitude, float $longitude): ?array
    {
        $db = Database::connection();
        $stmt = $db->prepare("SELECT id,crossing_number,crossing_name,status,latitude,longitude,geofence_radius_m FROM border_crossings WHERE load_id=? AND latitude IS NOT NULL AND longitude IS NOT NULL AND status NOT IN ('crossed','cancelled','rejected_returned') ORDER BY COALESCE(planned_at,created_at) LIMIT 1");
        $stmt->execute([$loadId]);
        $crossing = $stmt->fetch();
        if (!$crossing) return null;

        $distance = $this->distanceMeters($latitude, $longitude, (float)$crossing['latitude'], (float)$crossing['longitude']);
        $radius = max(100, (int)$crossing['geofence_radius_m']);
        $newStatus = null;

        if ($distance <= $radius && !in_array($crossing['status'], ['arrived_at_border','waiting_in_queue','customs_inspection','on_hold','cleared'], true)) {
            $newStatus = 'arrived_at_border';
        } elseif ($distance <= $radius * 3 && in_array($crossing['status'], ['ready_to_cross','not_started','documents_submitted','broker_reviewing'], true)) {
            $newStatus = 'driver_en_route';
        }

        if ($newStatus) {
            (new BorderCrossing())->setStatus((int)$crossing['id'], $newStatus);
            $message = $crossing['crossing_number'] . ' — ' . $crossing['crossing_name'] . ' is now ' . readable_status($newStatus) . '.';
            notify_roles(['owner','admin','dispatcher'], 'Border geofence update', $message, '/borders/show?id='.(int)$crossing['id'], $newStatus==='arrived_at_border'?'warning':'info');
            log_activity('border_geofence', $message);
        }

        return ['crossing_id'=>(int)$crossing['id'], 'distance_m'=>(int)round($distance), 'status'=>$newStatus ?: $crossing['status']];
    }

    private function distanceMeters(float $lat1, float $lon1, float $lat2, float $lon2): float
    {
        $earth = 6371000;
        $phi1 = deg2rad($lat1);
        $phi2 = deg2rad($lat2);
        $deltaPhi = deg2rad($lat2 - $lat1);
        $deltaLambda = deg2rad($lon2 - $lon1);
        $a = sin($deltaPhi / 2) ** 2 + cos($phi1) * cos($phi2) * sin($deltaLambda / 2) ** 2;
        return $earth * 2 * atan2(sqrt($a), sqrt(1 - $a));
    }

    private function number($value): ?float
    {
        return $value !== null && $value !== '' && is_numeric($value) ? (float)$value : null;
    }
}
