<?php
declare(strict_types=1);
require_once '../config/database.php';
require_once '../app/helpers/rbac.php';
require_once '../app/helpers/audit.php';
require_once '../app/helpers/api_security.php';

header('Content-Type: application/json');

requireApiLogin();
enforceCsrfForUnsafeRequest();

$action = $_GET['action'] ?? '';

try {
    switch ($action) {
        case 'recommend':
            handleRecommend($pdo);
            break;
        case 'accept':
            handleAccept($pdo);
            break;
        case 'weights':
            handleWeights($pdo);
            break;
        case 'save_weights':
            handleSaveWeights($pdo);
            break;
        default:
            respond(['status' => 'error', 'message' => 'Invalid action']);
    }
} catch (Throwable $e) {
    respond(['status' => 'error', 'message' => $e->getMessage()]);
}

function respond(array $payload): void { apiRespond($payload); }

function handleWeights(PDO $pdo): void
{
    $stmt = $pdo->query("SELECT criterion, weight FROM allocation_weights WHERE is_active = 1 ORDER BY id");
    $weights = $stmt->fetchAll(PDO::FETCH_ASSOC);
    respond(['status' => 'success', 'data' => $weights]);
}

function handleSaveWeights(PDO $pdo): void
{
    if (!isAdmin()) respond(['status' => 'error', 'message' => 'Unauthorized']);

    $weights = $_POST['weights'] ?? null;
    if (!$weights || !is_array($weights)) {
        respond(['status' => 'error', 'message' => 'Invalid weights data']);
    }

    foreach ($weights as $criterion => $weight) {
        $w = (int)$weight;
        if ($w < 0) $w = 0;
        if ($w > 100) $w = 100;
        $stmt = $pdo->prepare("UPDATE allocation_weights SET weight = ? WHERE criterion = ?");
        $stmt->execute([$w, $criterion]);
    }

    respond(['status' => 'success', 'message' => 'Weights updated']);
}

function handleRecommend(PDO $pdo): void
{
    if (!isAdmin() && !can('allocation.ai_view') && !can('vehicle_request.assign')) {
        respond(['status' => 'error', 'message' => 'Unauthorized']);
    }

    $requestId = (int)($_GET['request_id'] ?? 0);
    if ($requestId <= 0) {
        respond(['status' => 'error', 'message' => 'Request ID required']);
    }

    // Load the request
    $stmt = $pdo->prepare("SELECT * FROM vehicle_requests WHERE id = ? AND deleted_at IS NULL");
    $stmt->execute([$requestId]);
    $request = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$request) respond(['status' => 'error', 'message' => 'Request not found']);

    // Load scoring weights
    $weightStmt = $pdo->query("SELECT criterion, weight FROM allocation_weights WHERE is_active = 1");
    $weights = [];
    while ($w = $weightStmt->fetch(PDO::FETCH_ASSOC)) {
        $weights[$w['criterion']] = (int)$w['weight'];
    }

    // Get all available vehicles (Active status)
    $vehicles = [];
    $vStmt = $pdo->query("
        SELECT v.*,
               v.vehicle_status, v.seats, v.vehicle_type, v.current_odometer,
               COALESCE(v.standard_fuel_consumption_l_per_100km, 0) AS fuel_consumption
        FROM vehicles v
        WHERE v.deleted_at IS NULL
          AND (v.vehicle_status IS NULL OR v.vehicle_status = 'Active')
        ORDER BY v.plate_number
    ");
    $allVehicles = $vStmt->fetchAll(PDO::FETCH_ASSOC);

    if (empty($allVehicles)) {
        respond(['status' => 'success', 'data' => [], 'message' => 'No available vehicles found']);
    }

    $recommendations = [];

    foreach ($allVehicles as $vehicle) {
        $scores = [];
        $details = [];

        // 1. Availability Score (0-30)
        $availData = scoreAvailability($pdo, $vehicle, $request);
        $scores['availability'] = $availData['score'];
        $details['availability'] = $availData['detail'];

        // 2. Vehicle Type / Capacity Match (0-20)
        $typeData = scoreTypeMatch($vehicle, $request);
        $scores['type_match'] = $typeData['score'];
        $details['type_match'] = $typeData['detail'];

        // 3. Fuel Sufficiency (0-15)
        $fuelData = scoreFuel($pdo, $vehicle);
        $scores['fuel'] = $fuelData['score'];
        $details['fuel'] = $fuelData['detail'];

        // 4. Maintenance Status (0-15)
        $maintData = scoreMaintenance($pdo, $vehicle);
        $scores['maintenance'] = $maintData['score'];
        $details['maintenance'] = $maintData['detail'];

        // 5. Vehicle Condition (0-10)
        $condData = scoreCondition($pdo, $vehicle);
        $scores['condition'] = $condData['score'];
        $details['condition'] = $condData['detail'];

        // 6. Utilization Balance (0-10)
        $utilData = scoreUtilization($pdo, $vehicle);
        $scores['utilization'] = $utilData['score'];
        $details['utilization'] = $utilData['detail'];

        // Weighted total
        $total = 0;
        $maxPossible = 0;
        foreach ($weights as $criterion => $weight) {
            $maxPossible += $weight;
            if (isset($scores[$criterion])) {
                $baseMax = maxScoreFor($criterion);
                $normalized = $baseMax > 0 ? ($scores[$criterion] / $baseMax) : 0;
                $total += $normalized * $weight;
            }
        }

        $finalScore = $maxPossible > 0 ? round(($total / $maxPossible) * 100, 1) : 0;

        $recommendations[] = [
            'vehicle_id' => (int)$vehicle['id'],
            'plate_number' => $vehicle['plate_number'] ?? 'Unknown',
            'vehicle_type' => $vehicle['vehicle_type'] ?? '',
            'vehicle_model' => $vehicle['vehicle_model'] ?? '',
            'seats' => (int)($vehicle['seats'] ?? 4),
            'score' => $finalScore,
            'availability_score' => round($scores['availability'], 1),
            'type_match_score' => round($scores['type_match'], 1),
            'fuel_score' => round($scores['fuel'], 1),
            'maintenance_score' => round($scores['maintenance'], 1),
            'condition_score' => round($scores['condition'], 1),
            'utilization_score' => round($scores['utilization'], 1),
            'details' => $details
        ];
    }

    // Sort by score descending
    usort($recommendations, fn($a, $b) => $b['score'] <=> $a['score']);

    // Assign rank
    $top = [];
    foreach (array_slice($recommendations, 0, 5) as $i => $rec) {
        $rec['rank'] = $i + 1;
        $top[] = $rec;
    }

    // Log recommendations to DB
    $pdo->prepare("DELETE FROM allocation_recommendations WHERE request_id = ?")->execute([$requestId]);
    $insertStmt = $pdo->prepare("
        INSERT INTO allocation_recommendations
        (request_id, vehicle_id, score, availability_score, type_match_score, fuel_score, maintenance_score, condition_score, utilization_score, rank_position)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    ");
    foreach ($top as $rec) {
        $insertStmt->execute([
            $requestId,
            $rec['vehicle_id'],
            $rec['score'],
            $rec['availability_score'],
            $rec['type_match_score'],
            $rec['fuel_score'],
            $rec['maintenance_score'],
            $rec['condition_score'],
            $rec['utilization_score'],
            $rec['rank']
        ]);
    }

    respond([
        'status' => 'success',
        'data' => $top,
        'request' => [
            'passenger_count' => (int)$request['passenger_count'],
            'journey_purpose' => $request['journey_purpose'],
            'start_date' => $request['start_date'],
            'end_date' => $request['end_date'],
            'priority' => $request['priority']
        ]
    ]);
}

function handleAccept(PDO $pdo): void
{
    if (!isAdmin() && !can('vehicle_request.assign')) {
        respond(['status' => 'error', 'message' => 'Unauthorized']);
    }

    $requestId = (int)($_POST['request_id'] ?? 0);
    $vehicleId = (int)($_POST['vehicle_id'] ?? 0);

    if ($requestId <= 0 || $vehicleId <= 0) {
        respond(['status' => 'error', 'message' => 'Invalid request or vehicle']);
    }

    // Mark the selected recommendation
    $stmt = $pdo->prepare("UPDATE allocation_recommendations SET selected = 1 WHERE request_id = ? AND vehicle_id = ?");
    $stmt->execute([$requestId, $vehicleId]);

    respond(['status' => 'success', 'message' => 'Recommendation accepted']);
}

function maxScoreFor(string $criterion): int
{
    return match ($criterion) {
        'availability' => 100,
        'type_match' => 100,
        'fuel' => 100,
        'maintenance' => 100,
        'condition' => 100,
        'utilization' => 100,
        default => 10,
    };
}

// ====== Scoring Functions ======

function scoreAvailability(PDO $pdo, array $vehicle, array $request): array
{
    $start = $request['start_date'] ?? date('Y-m-d');
    $end = $request['end_date'] ?? $start;

    // Check overlapping trips
    $tripStmt = $pdo->prepare("
        SELECT COUNT(*) FROM trips
        WHERE vehicle_id = ? AND deleted_at IS NULL
          AND status = 'active'
          AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)
    ");
    $tripStmt->execute([$vehicle['id'], $end, $start]);
    $activeTrips = (int)$tripStmt->fetchColumn();

    // Check overlapping assignments
    $assignStmt = $pdo->prepare("
        SELECT COUNT(*) FROM vehicle_assignments
        WHERE vehicle_id = ? AND deleted_at IS NULL
          AND status NOT IN ('ended', 'returned')
          AND (assignment_end IS NULL OR assignment_end >= ?)
    ");
    $assignStmt->execute([$vehicle['id'], $start]);
    $activeAssignments = (int)$assignStmt->fetchColumn();

    // Check overlapping requests
    $reqStmt = $pdo->prepare("
        SELECT COUNT(*) FROM vehicle_requests
        WHERE assigned_vehicle_id = ? AND deleted_at IS NULL
          AND id != ? AND status IN ('assigned', 'in_progress', 'approved')
          AND start_date <= ? AND (end_date IS NULL OR end_date >= ?)
    ");
    $reqStmt->execute([$vehicle['id'], $request['id'], $end, $start]);
    $overlappingRequests = (int)$reqStmt->fetchColumn();

    $totalOverlaps = $activeTrips + $activeAssignments + $overlappingRequests;

    if ($totalOverlaps > 0) {
        return ['score' => 0, 'detail' => "Overlaps with {$totalOverlaps} existing booking(s)"];
    }

    return ['score' => 100, 'detail' => 'Fully available for requested period'];
}

function scoreTypeMatch(array $vehicle, array $request): array
{
    $passengers = (int)($request['passenger_count'] ?? 1);
    $seats = (int)($vehicle['seats'] ?? 4);
    $purpose = strtolower($request['journey_purpose'] ?? '');
    $vehicleType = strtolower($vehicle['vehicle_type'] ?? '');
    $vehicleUseType = strtolower($vehicle['vehicle_use_type'] ?? '');

    // Check seat capacity
    $seatScore = 0;
    if ($seats >= $passengers) {
        $seatScore = 50;
        if ($seats >= $passengers + 2) {
            $seatScore = 40; // Too many seats - slightly penalized for inefficiency
        }
    } else {
        return ['score' => 10, 'detail' => "Only {$seats} seats for {$passengers} passengers"];
    }

    // Check type suitability
    $typeScore = 0;
    $isStaff = str_contains($purpose, 'staff') || str_contains($purpose, 'personnel') || str_contains($purpose, 'training');
    $isCargo = str_contains($purpose, 'cargo') || str_contains($purpose, 'goods') || str_contains($purpose, 'delivery') || str_contains($purpose, 'material');
    $isField = str_contains($purpose, 'field') || str_contains($purpose, 'inspection') || str_contains($purpose, 'site');

    if ($isCargo && (str_contains($vehicleUseType, 'cargo') || str_contains($vehicleUseType, 'pickup') || str_contains($vehicleUseType, 'van'))) {
        $typeScore = 50;
    } elseif ($isField && (str_contains($vehicleUseType, 'suv') || str_contains($vehicleUseType, '4wd') || str_contains($vehicleUseType, 'pickup'))) {
        $typeScore = 50;
    } elseif ($isStaff && (str_contains($vehicleUseType, 'sedan') || str_contains($vehicleUseType, 'bus') || $seats >= $passengers)) {
        $typeScore = 50;
    } else {
        $typeScore = 30;
    }

    return ['score' => $seatScore + $typeScore, 'detail' => "{$seats} seats, vehicle type: {$vehicle['vehicle_type']}"];
}

function scoreFuel(PDO $pdo, array $vehicle): array
{
    $stmt = $pdo->prepare("
        SELECT fuel_purchase_liters, end_km, begin_km, naira_per_liter
        FROM vehicle_fuel_entries
        WHERE vehicle_id = ? AND deleted_at IS NULL
        ORDER BY record_date DESC LIMIT 3
    ");
    $stmt->execute([$vehicle['id']]);
    $entries = $stmt->fetchAll(PDO::FETCH_ASSOC);

    if (empty($entries)) {
        // No recent fuel data - use standard consumption
        $consumption = (float)($vehicle['standard_fuel_consumption_l_per_100km'] ?? 10);
        if ($consumption <= 0) $consumption = 10;
        return ['score' => 50, 'detail' => "No recent entries; est. {$consumption} L/100km"];
    }

    // Calculate average efficiency
    $totalKm = 0;
    $totalLiters = 0;
    foreach ($entries as $e) {
        $km = (int)($e['end_km'] ?? 0) - (int)($e['begin_km'] ?? 0);
        if ($km > 0) {
            $totalKm += $km;
            $totalLiters += (float)($e['fuel_purchase_liters'] ?? 0);
        }
    }

    if ($totalKm === 0 || $totalLiters === 0) {
        return ['score' => 50, 'detail' => 'Insufficient fuel data'];
    }

    $efficiency = $totalLiters / ($totalKm / 100);
    $baseline = (float)($vehicle['standard_fuel_consumption_l_per_100km'] ?? 10);
    if ($baseline <= 0) $baseline = 10;

    $ratio = $efficiency / $baseline;
    if ($ratio <= 1.0) {
        return ['score' => 100, 'detail' => "Efficient: {$efficiency} L/100km (baseline {$baseline})"];
    }
    if ($ratio <= 1.2) {
        return ['score' => 70, 'detail' => "Moderate: {$efficiency} L/100km (baseline {$baseline})"];
    }
    return ['score' => 30, 'detail' => "High consumption: {$efficiency} L/100km (baseline {$baseline})"];
}

function scoreMaintenance(PDO $pdo, array $vehicle): array
{
    $stmt = $pdo->prepare("
        SELECT due_date, due_mileage, interval_km, interval_days
        FROM service_intervals
        WHERE vehicle_id = ? AND is_active = 1 AND deleted_at IS NULL
        ORDER BY due_date ASC LIMIT 1
    ");
    $stmt->execute([$vehicle['id']]);
    $interval = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$interval) {
        // No intervals set - neutral score
        return ['score' => 70, 'detail' => 'No scheduled maintenance'];
    }

    $today = new DateTime();
    $score = 100;

    if (!empty($interval['due_date'])) {
        $due = new DateTime($interval['due_date']);
        $daysOverdue = (int)$today->diff($due)->format('%r%a');

        if ($daysOverdue < -30) {
            $score = 10;
            return ['score' => 10, 'detail' => 'Overdue by ' . abs($daysOverdue) . ' days'];
        }
        if ($daysOverdue < -7) {
            $score = 20;
        } elseif ($daysOverdue < 0) {
            $score = 40;
        } elseif ($daysOverdue <= 7) {
            $score = 60;
        } elseif ($daysOverdue <= 30) {
            $score = 80;
        }
    }

    // Check mileage-based intervals
    if (!empty($interval['due_mileage']) && !empty($vehicle['current_odometer'])) {
        $remainingKm = (int)$interval['due_mileage'] - (int)$vehicle['current_odometer'];
        if ($remainingKm < 0) {
            $score = min($score, 15);
            return ['score' => 15, 'detail' => 'Mileage interval exceeded by ' . abs($remainingKm) . ' km'];
        }
        if ($remainingKm < 500) {
            $score = min($score, 40);
        }
    }

    return ['score' => $score, 'detail' => $score >= 70 ? 'Maintenance up to date' : 'Maintenance due soon'];
}

function scoreCondition(PDO $pdo, array $vehicle): array
{
    // Check recent incidents (last 90 days)
    $incidentStmt = $pdo->prepare("
        SELECT COUNT(*) FROM incidents
        WHERE vehicle_id = ? AND deleted_at IS NULL
          AND incident_date >= DATE_SUB(CURDATE(), INTERVAL 90 DAY)
    ");
    $incidentStmt->execute([$vehicle['id']]);
    $recentIncidents = (int)$incidentStmt->fetchColumn();

    // Check recent maintenance jobs
    $maintStmt = $pdo->prepare("
        SELECT COUNT(*) FROM maintenance_jobs
        WHERE vehicle_id = ? AND deleted_at IS NULL
          AND complaint_date >= DATE_SUB(CURDATE(), INTERVAL 90 DAY)
          AND status NOT IN ('Closed', 'Completed')
    ");
    $maintStmt->execute([$vehicle['id']]);
    $openJobs = (int)$maintStmt->fetchColumn();

    $score = 100;
    $issues = [];

    if ($recentIncidents > 0) {
        $score -= min(40, $recentIncidents * 20);
        $issues[] = "{$recentIncidents} incident(s) in 90 days";
    }
    if ($openJobs > 0) {
        $score -= min(40, $openJobs * 20);
        $issues[] = "{$openJobs} open maintenance job(s)";
    }

    $detail = empty($issues) ? 'No recent issues' : implode('; ', $issues);
    return ['score' => max(10, $score), 'detail' => $detail];
}

function scoreUtilization(PDO $pdo, array $vehicle): array
{
    // Count trips in last 60 days to measure utilization
    $stmt = $pdo->prepare("
        SELECT COUNT(*) FROM trips
        WHERE vehicle_id = ? AND deleted_at IS NULL
          AND start_date >= DATE_SUB(CURDATE(), INTERVAL 60 DAY)
    ");
    $stmt->execute([$vehicle['id']]);
    $tripCount = (int)$stmt->fetchColumn();

    // Active days (fuel entries with different dates)
    $fuelStmt = $pdo->prepare("
        SELECT COUNT(DISTINCT record_date) FROM vehicle_fuel_entries
        WHERE vehicle_id = ? AND deleted_at IS NULL
          AND record_date >= DATE_SUB(CURDATE(), INTERVAL 60 DAY)
    ");
    $fuelStmt->execute([$vehicle['id']]);
    $activeDays = (int)$fuelStmt->fetchColumn();

    $usageMetric = max($tripCount, $activeDays);

    // Vehicles used 0-3 times in 60 days get high score (underutilized -> prefer them)
    // Vehicles used 10+ times get lower score
    if ($usageMetric <= 1) {
        return ['score' => 100, 'detail' => "Low usage: {$usageMetric} trips in 60 days"];
    }
    if ($usageMetric <= 3) {
        return ['score' => 80, 'detail' => "Moderate usage: {$usageMetric} trips in 60 days"];
    }
    if ($usageMetric <= 6) {
        return ['score' => 50, 'detail' => "Regular usage: {$usageMetric} trips in 60 days"];
    }
    return ['score' => 20, 'detail' => "High usage: {$usageMetric} trips in 60 days"];
}
