<?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';
require_once '../app/helpers/soft_delete.php';
require_once '../app/helpers/normalize.php';

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

requireApiLogin();
enforceCsrfForUnsafeRequest();

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

if (in_array($action, ['save', 'delete'], true)) {
    if (!isAdmin() && !can('vehicle.assign')) {
        respond(['status' => 'error', 'message' => 'Unauthorized']);
    }
} elseif (in_array($action, ['list', 'history', 'drivers', 'companies', 'assignment_report'], true)) {
    if (!isAdmin() && !can('vehicle.view') && !can('vehicle.assign') && !can('report.view')) {
        respond(['status' => 'error', 'message' => 'Unauthorized']);
    }
}

try {
    switch ($action) {
        case 'list':
            handleList($pdo);
            break;
        case 'save':
            handleSave($pdo);
            break;
        case 'delete':
            handleDelete($pdo);
            break;
        case 'restore':
            handleRestoreAssignment($pdo);
            break;
        case 'history':
            handleHistory($pdo);
            break;
        case 'drivers':
            handleDrivers($pdo);
            break;
        case 'companies':
            handleCompanies($pdo);
            break;
        case 'assignment_report':
            handleAssignmentReport($pdo);
            break;
        default:
            respond(['status' => 'error', 'message' => 'Invalid action']);
    }
} catch (Throwable $e) {
    respond(['status' => 'error', 'message' => $e->getMessage()]);
}

function expireOverdueAssignments(PDO $pdo): void
{
    $pdo->prepare("UPDATE vehicle_assignments SET status = 'inactive' WHERE status = 'active' AND assignment_end IS NOT NULL AND assignment_end < CURDATE()")->execute();
}

function getAssignmentList(PDO $pdo, int $vehicleId): array
{
    expireOverdueAssignments($pdo);
    $stmt = $pdo->prepare("
        SELECT
            va.*,
            d.full_name AS driver_name,
            d.driver_id_code AS driver_code,
            fc.name AS company_name,
            fc.code AS company_code
        FROM vehicle_assignments va
        LEFT JOIN drivers d ON d.id = va.driver_id
        LEFT JOIN fuel_companies fc ON fc.id = va.company_id
        WHERE va.vehicle_id = ? AND va.deleted_at IS NULL
        ORDER BY va.assignment_start DESC
        LIMIT 100
    ");
    $stmt->execute([$vehicleId]);
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

function getAssignmentHistory(PDO $pdo, int $vehicleId): array
{
    expireOverdueAssignments($pdo);
    $stmt = $pdo->prepare("
        SELECT
            va.*,
            d.full_name AS driver_name,
            d.driver_id_code AS driver_code,
            fc.name AS company_name,
            fc.code AS company_code,
            DATEDIFF(COALESCE(va.assignment_end, CURDATE()), va.assignment_start) + 1 AS duration_days
        FROM vehicle_assignments va
        LEFT JOIN drivers d ON d.id = va.driver_id
        LEFT JOIN fuel_companies fc ON fc.id = va.company_id
        WHERE va.vehicle_id = ? AND va.deleted_at IS NULL
        ORDER BY va.assignment_start DESC
        LIMIT 100
    ");
    $stmt->execute([$vehicleId]);
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

function handleList(PDO $pdo): void
{
    $vehicleId = (int)($_GET['vehicle_id'] ?? 0);
    if ($vehicleId <= 0) {
        respond(['status' => 'error', 'message' => 'Invalid vehicle ID']);
    }

    respond(['status' => 'success', 'data' => getAssignmentList($pdo, $vehicleId)]);
}

function handleHistory(PDO $pdo): void
{
    $vehicleId = (int)($_GET['vehicle_id'] ?? 0);
    if ($vehicleId <= 0) {
        respond(['status' => 'error', 'message' => 'Invalid vehicle ID']);
    }

    respond(['status' => 'success', 'data' => getAssignmentHistory($pdo, $vehicleId)]);
}

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

    $id = isset($_POST['assignment_id']) ? (int)$_POST['assignment_id'] : 0;
    $vehicleId = (int)($_POST['vehicle_id'] ?? 0);
    $driverId = (int)($_POST['driver_id'] ?? 0);
    $companyId = isset($_POST['company_id']) ? (int)$_POST['company_id'] : null;
    $startDate = trim((string)($_POST['start_date'] ?? ''));
    $endDate = trim((string)($_POST['end_date'] ?? ''));
    $status = trim((string)($_POST['status'] ?? 'active'));
    $leasedDays = isset($_POST['leased_days']) && $_POST['leased_days'] !== '' ? (int)$_POST['leased_days'] : null;

    // Auto-set inactive if end date is in the past
    $endDateObj = $endDate !== '' ? $endDate : null;
    if ($endDateObj !== null && $endDateObj < date('Y-m-d')) {
        $status = 'inactive';
    }

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

    if ($driverId <= 0) {
        respond(['status' => 'error', 'message' => 'Driver is required']);
    }

    if ($startDate === '') {
        respond(['status' => 'error', 'message' => 'Start date is required']);
    }

    // Validate date formats
    if (!validateDate($startDate)) {
        respond(['status' => 'error', 'message' => 'Invalid start date format']);
    }

    if ($endDate !== '' && !validateDate($endDate)) {
        respond(['status' => 'error', 'message' => 'Invalid end date format']);
    }

    // Check for overlapping assignments
    $overlapError = checkAssignmentOverlap($pdo, $vehicleId, $driverId, $startDate, $endDateObj, $id);
    if ($overlapError !== null) {
        respond(['status' => 'error', 'message' => $overlapError]);
    }

    $payload = [
        'vehicle_id' => $vehicleId,
        'driver_id' => $driverId,
        'company_id' => $companyId,
        'assignment_start' => $startDate,
        'assignment_end' => $endDate === '' ? null : $endDate,
        'status' => $status,
        'leased_days' => $leasedDays,
        'updated_at' => date('Y-m-d H:i:s')
    ];

    if ($id > 0) {
        // Update existing assignment
        $before = fetchAssignment($pdo, $id);
        if (!$before) {
            respond(['status' => 'error', 'message' => 'Assignment not found']);
        }

        $setParts = [];
        foreach (array_keys($payload) as $column) {
            $setParts[] = $column . ' = :' . $column;
        }
        $payload['id'] = $id;

        $stmt = $pdo->prepare("
            UPDATE vehicle_assignments
            SET " . implode(", ", $setParts) . "
            WHERE id = :id AND deleted_at IS NULL
        ");
        $stmt->execute($payload);

        audit($pdo, (int)$_SESSION['user_id'], 'update', 'vehicle_assignment', $id, 'Assignment updated', $before, $payload);
    } else {
        // Create new assignment
        $payload['created_at'] = date('Y-m-d H:i:s');

        $columns = implode(', ', array_keys($payload));
        $values = implode(', ', array_map(static fn(string $column): string => ':' . $column, array_keys($payload)));

        $stmt = $pdo->prepare("
            INSERT INTO vehicle_assignments ({$columns})
            VALUES ({$values})
        ");
        $stmt->execute($payload);

        $newId = (int)$pdo->lastInsertId();
        audit($pdo, (int)$_SESSION['user_id'], 'create', 'vehicle_assignment', $newId, 'Assignment created', null, $payload);
    }

    // Return updated list and history so JS can skip separate fetch calls
    respond([
        'status' => 'success',
        'assignment' => getAssignmentList($pdo, $vehicleId),
        'history' => getAssignmentHistory($pdo, $vehicleId)
    ]);
}

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

    $id = (int)($_POST['assignment_id'] ?? 0);
    if ($id <= 0) {
        respond(['status' => 'error', 'message' => 'Invalid assignment ID']);
    }

    $before = fetchAssignment($pdo, $id);
    if (!$before) {
        respond(['status' => 'error', 'message' => 'Assignment not found']);
    }

    softDelete($pdo, 'vehicle_assignments', $id);

    audit($pdo, (int)$_SESSION['user_id'], 'delete', 'vehicle_assignment', $id, 'Assignment deleted', $before, null);
    respond(['status' => 'success']);
}

function handleCompanies(PDO $pdo): void
{
    $stmt = $pdo->prepare("
        SELECT
            id,
            name,
            code
        FROM fuel_companies
        WHERE is_active = 1 AND deleted_at IS NULL
        ORDER BY name
    ");
    $stmt->execute();
    $companies = $stmt->fetchAll(PDO::FETCH_ASSOC);

    respond(['status' => 'success', 'data' => $companies]);
}

function handleDrivers(PDO $pdo): void
{
    $search = trim((string)($_GET['search'] ?? ''));
    $sql = "
        SELECT
            id,
            full_name,
            driver_id_code AS driver_code,
            driver_status
        FROM drivers
        WHERE active_status = 1 AND deleted_at IS NULL
    ";

    if ($search !== '') {
        $sql .= " AND (full_name LIKE :search OR driver_id_code LIKE :search OR driver_status LIKE :search)";
    }

    $sql .= " ORDER BY full_name ASC LIMIT 50";

    $stmt = $pdo->prepare($sql);
    if ($search !== '') {
        $stmt->bindValue(':search', '%' . $search . '%', PDO::PARAM_STR);
    }
    $stmt->execute();

    $drivers = $stmt->fetchAll(PDO::FETCH_ASSOC);
    respond(['status' => 'success', 'data' => $drivers]);
}

function fetchAssignment(PDO $pdo, int $id): ?array
{
    $stmt = $pdo->prepare("SELECT * FROM vehicle_assignments WHERE id = ? AND deleted_at IS NULL");
    $stmt->execute([$id]);
    return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
}

function validateDate(string $date): bool
{
    $d = DateTime::createFromFormat('Y-m-d', $date);
    return $d && $d->format('Y-m-d') === $date;
}

function checkAssignmentOverlap(PDO $pdo, int $vehicleId, int $driverId, string $startDate, ?string $endDate, int $excludeId = 0): ?string
{
    // Returns null if no overlap, or a descriptive error message on conflict
    $query = "
        SELECT va.id, va.vehicle_id, va.driver_id, va.assignment_start, va.assignment_end,
               d.full_name AS driver_name,
               v.plate_number AS vehicle_plate,
               CASE
                   WHEN va.vehicle_id = ? AND va.driver_id = ? THEN 'vehicle and driver'
                   WHEN va.vehicle_id = ? THEN 'vehicle'
                   ELSE 'driver'
               END AS conflict_type
        FROM vehicle_assignments va
        LEFT JOIN drivers d ON d.id = va.driver_id
        LEFT JOIN vehicles v ON v.id = va.vehicle_id
        WHERE va.id != ? AND va.deleted_at IS NULL
        AND (va.assignment_end IS NULL OR va.assignment_end >= va.assignment_start)
        AND (
            (va.vehicle_id = ? AND va.assignment_start <= COALESCE(?, '9999-12-31') AND ? <= COALESCE(va.assignment_end, '9999-12-31'))
            OR
            (va.driver_id = ? AND va.assignment_start <= COALESCE(?, '9999-12-31') AND ? <= COALESCE(va.assignment_end, '9999-12-31'))
        )
        LIMIT 1
    ";

    $params = [
        $vehicleId, $driverId,
        $vehicleId,
        $excludeId,
        $vehicleId, $endDate, $startDate,
        $driverId, $endDate, $startDate
    ];

    $stmt = $pdo->prepare($query);
    $stmt->execute($params);
    $conflict = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$conflict) {
        return null;
    }

    $conflictType = $conflict['conflict_type'];
    $driverName = $conflict['driver_name'] ?? "Driver #{$conflict['driver_id']}";
    $vehiclePlate = $conflict['vehicle_plate'] ?? "Vehicle #{$conflict['vehicle_id']}";
    $conflictStart = $conflict['assignment_start'];
    $conflictEnd = $conflict['assignment_end'] ?? 'ongoing';

    if ($conflictType === 'vehicle') {
        return "Vehicle {$vehiclePlate} already has an assignment (id={$conflict['id']}) from {$conflictStart} to {$conflictEnd}";
    }
    if ($conflictType === 'driver') {
        return "Driver {$driverName} already has an assignment on {$vehiclePlate} (id={$conflict['id']}) from {$conflictStart} to {$conflictEnd}";
    }
    return "Assignment dates overlap with existing assignment (id={$conflict['id']}): {$vehiclePlate} / {$driverName} from {$conflictStart} to {$conflictEnd}";
}

function handleRestoreAssignment(PDO $pdo): void
{
    $id = (int)($_POST['assignment_id'] ?? 0);
    if ($id <= 0) {
        respond(['status' => 'error', 'message' => 'Invalid assignment ID']);
    }
    restoreRecord($pdo, 'vehicle_assignments', $id);
    audit($pdo, (int)$_SESSION['user_id'], 'restore', 'vehicle_assignment', $id, 'Assignment restored', null, null);
    respond(['status' => 'success']);
}

function handleAssignmentReport(PDO $pdo): void
{
    expireOverdueAssignments($pdo);

    $search = trim((string)($_GET['search'] ?? ''));
    $limit = min((int)($_GET['limit'] ?? 500), 2000);
    $offset = max((int)($_GET['offset'] ?? 0), 0);

    $where = [];
    $params = [];

    if ($search !== '') {
        $where[] = "(d.full_name LIKE :search OR d.driver_id_code LIKE :search2 OR v.plate_number LIKE :search3 OR v.vehicle_model LIKE :search4)";
        $params['search'] = "%{$search}%";
        $params['search2'] = "%{$search}%";
        $params['search3'] = "%{$search}%";
        $params['search4'] = "%{$search}%";
    }

    $whereSql = $where ? 'AND ' . implode(' AND ', $where) : '';

    $countStmt = $pdo->prepare("
        SELECT COUNT(*) FROM vehicle_assignments va
        INNER JOIN drivers d ON d.id = va.driver_id
        INNER JOIN vehicles v ON v.id = va.vehicle_id
        WHERE va.deleted_at IS NULL {$whereSql}
    ");
    $countStmt->execute($params);
    $total = (int)$countStmt->fetchColumn();

    $kpiStmt = $pdo->prepare("
        SELECT
            COUNT(*) AS total,
            SUM(CASE WHEN va.status = 'active' THEN 1 ELSE 0 END) AS active_count,
            SUM(CASE WHEN va.status != 'active' THEN 1 ELSE 0 END) AS inactive_count,
            COUNT(DISTINCT va.driver_id) AS unique_drivers,
            COUNT(DISTINCT va.vehicle_id) AS unique_vehicles
        FROM vehicle_assignments va
        INNER JOIN drivers d ON d.id = va.driver_id
        INNER JOIN vehicles v ON v.id = va.vehicle_id
        WHERE va.deleted_at IS NULL {$whereSql}
    ");
    $kpiStmt->execute($params);
    $kpis = $kpiStmt->fetch(PDO::FETCH_ASSOC);

    $stmt = $pdo->prepare("
        SELECT
            va.id, va.driver_id, va.vehicle_id, va.company_id,
            va.assignment_start, va.assignment_end,
            va.status, va.leased_days, va.created_at, va.updated_at,
            d.full_name AS driver_name,
            d.driver_id_code AS driver_code,
            v.plate_number AS vehicle_plate,
            v.vehicle_model AS vehicle_model,
            fc.name AS company_name,
            DATEDIFF(COALESCE(va.assignment_end, CURDATE()), va.assignment_start) + 1 AS duration_days
        FROM vehicle_assignments va
        INNER JOIN drivers d ON d.id = va.driver_id
        INNER JOIN vehicles v ON v.id = va.vehicle_id
        LEFT JOIN fuel_companies fc ON fc.id = va.company_id
        WHERE va.deleted_at IS NULL {$whereSql}
        ORDER BY va.assignment_start DESC
        LIMIT {$limit} OFFSET {$offset}
    ");
    $stmt->execute($params);
    $assignments = $stmt->fetchAll(PDO::FETCH_ASSOC);

    respond([
        'status' => 'success',
        'data' => [
            'assignments' => $assignments,
            'kpis' => $kpis ?: ['total' => 0, 'active_count' => 0, 'inactive_count' => 0, 'unique_drivers' => 0, 'unique_vehicles' => 0],
            'total' => $total,
            'limit' => $limit,
            'offset' => $offset
        ]
    ]);
}

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