<?php
declare(strict_types=1);
register_shutdown_function(function () {
    $err = error_get_last();
    if ($err && in_array($err['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR], true)) {
        echo json_encode(['status' => 'error', 'message' => 'Fatal: ' . $err['message'] . ' in ' . basename($err['file']) . ':' . $err['line']]);
    }
});

try {
    require_once '../config/database.php';
    require_once '../app/helpers/rbac.php';
    require_once '../app/helpers/audit.php';
    require_once '../app/helpers/workflow.php';
    require_once '../app/helpers/api_security.php';
    require_once '../app/helpers/soft_delete.php';
    require_once '../app/helpers/notification.php';

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

    requireApiLogin();
    enforceCsrfForUnsafeRequest();

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

    switch ($action) {
        case 'list': handleList($pdo); break;
        case 'save': handleSave($pdo); break;
        case 'submit': handleSubmit($pdo); break;
        case 'approve': handleApprove($pdo); break;
        case 'reject': handleReject($pdo); break;
        case 'return': handleReturn($pdo); break;
        case 'assign': handleAssign($pdo); break;
        case 'delete': handleDelete($pdo); break;
        case 'reference': handleReference($pdo); break;
        default: respond(['status' => 'error', 'message' => 'Unknown action']);
    }
} catch (Throwable $e) {
    $msg = $e->getMessage();
    $file = basename($e->getFile());
    $line = $e->getLine();
    respond(['status' => 'error', 'message' => "{$msg} in {$file}:{$line}"]);
}

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

function doAudit(PDO $pdo, string $action, string $description, ?int $recordId = null): void {
    audit($pdo, (int)($_SESSION['user_id'] ?? 0), $action, 'vehicle_request', $recordId ?? 0, $description, null, null);
}

function ensureTables(PDO $pdo): void {
    $pdo->exec("CREATE TABLE IF NOT EXISTS vehicle_requests (
        id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        request_number VARCHAR(30) NOT NULL UNIQUE,
        staff_id BIGINT NOT NULL,
        pickup_location VARCHAR(255),
        destination VARCHAR(255),
        journey_purpose TEXT,
        start_date DATE,
        end_date DATE,
        priority ENUM('low','normal','high','urgent') DEFAULT 'normal',
        passenger_count INT DEFAULT 1,
        supporting_document VARCHAR(255),
        status ENUM('draft','pending','approved','rejected','returned','assigned','in_progress','completed','cancelled') DEFAULT 'draft',
        assigned_vehicle_id BIGINT UNSIGNED NULL,
        assigned_driver_id BIGINT UNSIGNED NULL,
        notes TEXT,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        deleted_at TIMESTAMP NULL DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
    $pdo->exec("CREATE TABLE IF NOT EXISTS vehicle_request_approvals (
        id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        request_id BIGINT UNSIGNED NOT NULL,
        step_order INT NOT NULL,
        step_label VARCHAR(100) NOT NULL,
        approver_role VARCHAR(50) NOT NULL,
        approver_id BIGINT NULL,
        status ENUM('pending','approved','rejected','returned') DEFAULT 'pending',
        comment TEXT,
        acted_at TIMESTAMP NULL DEFAULT NULL,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (request_id) REFERENCES vehicle_requests(id) ON DELETE CASCADE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
    // Seed permissions if missing
    $perms = [
        'vehicle_request.create' => 'Submit vehicle requests',
        'vehicle_request.view' => 'View vehicle requests',
        'vehicle_request.approve' => 'Approve vehicle requests',
        'vehicle_request.assign' => 'Assign vehicle and driver to requests',
    ];
    foreach ($perms as $code => $desc) {
        $pdo->prepare("INSERT IGNORE INTO permissions (code, description) VALUES (?, ?)")->execute([$code, $desc]);
        $pid = $pdo->lastInsertId();
        if ($pid) {
            $pdo->prepare("INSERT IGNORE INTO role_permissions (role_id, permission_id) VALUES (1, ?)")->execute([$pid]);
            $pdo->prepare("INSERT IGNORE INTO role_permissions (role_id, permission_id) VALUES (6, ?)")->execute([$pid]);
            $pdo->prepare("INSERT IGNORE INTO role_permissions (role_id, permission_id) VALUES (7, ?)")->execute([$pid]);
        }
    }
}

function generateRequestNumber(PDO $pdo): string {
    $prefix = 'VR-' . date('Y') . '-';
    $stmt = $pdo->prepare("SELECT COUNT(*) FROM vehicle_requests WHERE request_number LIKE :prefix AND deleted_at IS NULL");
    $stmt->execute(['prefix' => $prefix . '%']);
    $count = (int)$stmt->fetchColumn() + 1;
    return $prefix . str_pad((string)$count, 5, '0', STR_PAD_LEFT);
}

function handleList(PDO $pdo): void
{
    ensureTables($pdo);
    $userId = (int)($_SESSION['user_id'] ?? 0);

    $where = ['vr.deleted_at IS NULL'];
    $params = [];

    // Staff sees own requests; approvers see all pending + their own
    $canApprove = isAdmin() || can('vehicle_request.approve');
    $canView = isAdmin() || can('vehicle_request.view');

    if (!$canView && !$canApprove) {
        $where[] = 'vr.staff_id = :staff_id';
        $params['staff_id'] = $userId;
    } elseif (!empty($_GET['mine']) && $_GET['mine'] === '1') {
        $where[] = 'vr.staff_id = :staff_id';
        $params['staff_id'] = $userId;
    }

    if (!empty($_GET['status'])) {
        $where[] = 'vr.status = :status';
        $params['status'] = $_GET['status'];
    }
    if (!empty($_GET['search'])) {
        $q = '%' . $_GET['search'] . '%';
        $where[] = '(vr.request_number LIKE :q1 OR vr.journey_purpose LIKE :q2 OR vr.pickup_location LIKE :q3 OR vr.destination LIKE :q4)';
        $params['q1'] = $q; $params['q2'] = $q; $params['q3'] = $q; $params['q4'] = $q;
    }
    if (!empty($_GET['date_from'])) {
        $where[] = 'vr.start_date >= :date_from';
        $params['date_from'] = $_GET['date_from'];
    }
    if (!empty($_GET['date_to'])) {
        $where[] = 'vr.start_date <= :date_to';
        $params['date_to'] = $_GET['date_to'];
    }

    $sql = "SELECT vr.*, u.name AS staff_name,
            v.plate_number AS assigned_vehicle_plate,
            d.full_name AS assigned_driver_name,
            (SELECT COUNT(*) FROM workflow_run_steps rs JOIN workflow_runs r ON r.id = rs.run_id WHERE r.module = 'vehicle_request' AND r.record_id = vr.id AND rs.status = 'pending') AS pending_approvals,
            (SELECT COUNT(*) FROM workflow_run_steps rs JOIN workflow_runs r ON r.id = rs.run_id WHERE r.module = 'vehicle_request' AND r.record_id = vr.id AND rs.status = 'approved') AS approved_approvals,
            (SELECT COUNT(*) FROM workflow_run_steps rs JOIN workflow_runs r ON r.id = rs.run_id WHERE r.module = 'vehicle_request' AND r.record_id = vr.id) AS total_approvals
            FROM vehicle_requests vr
            LEFT JOIN users u ON u.id = vr.staff_id
            LEFT JOIN vehicles v ON v.id = vr.assigned_vehicle_id
            LEFT JOIN drivers d ON d.id = vr.assigned_driver_id
            WHERE " . implode(' AND ', $where) . "
            ORDER BY vr.created_at DESC LIMIT 200";
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    $rows = $stmt->fetchAll();

    // Get pending approvals for current user (for badge counts)
    $pendingStmt = $pdo->prepare("SELECT COUNT(*) FROM workflow_run_steps rs
        JOIN workflow_runs r ON r.id = rs.run_id
        JOIN vehicle_requests vr ON vr.id = r.record_id AND r.module = 'vehicle_request' AND vr.deleted_at IS NULL AND vr.status = 'pending'
        WHERE rs.status = 'pending' AND (rs.assigned_user_id = :uid1 OR rs.delegate_user_id = :uid2)");
    $pendingStmt->execute(['uid1' => $userId, 'uid2' => $userId]);
    $pendingCount = (int)$pendingStmt->fetchColumn();

    respond(['status' => 'success', 'data' => $rows, 'pending_approval_count' => $pendingCount]);
}

function handleSave(PDO $pdo): void
{
    ensureTables($pdo);
    // Any logged-in user can create a request (staff functionality).
    // Approving and assigning are restricted separately.
    if (!isAdmin() && !can('vehicle_request.view') && !can('vehicle_request.create')) {
        respond(['status' => 'error', 'message' => 'Unauthorized']);
    }

    $id = (int)($_POST['id'] ?? 0);
    $userId = (int)($_SESSION['user_id'] ?? 0);

    $data = [
        'pickup_location' => trim($_POST['pickup_location'] ?? ''),
        'destination' => trim($_POST['destination'] ?? ''),
        'journey_purpose' => trim($_POST['journey_purpose'] ?? ''),
        'start_date' => $_POST['start_date'] ?? null,
        'end_date' => $_POST['end_date'] ?? null,
        'priority' => $_POST['priority'] ?? 'normal',
        'passenger_count' => (int)($_POST['passenger_count'] ?? 1),
        'notes' => trim($_POST['notes'] ?? ''),
    ];

    if (!$data['start_date']) respond(['status' => 'error', 'message' => 'Start date is required']);
    if (!$data['journey_purpose']) respond(['status' => 'error', 'message' => 'Journey purpose is required']);

    if ($id > 0) {
        // Only allow editing if status is draft or returned
        $check = $pdo->prepare("SELECT status, staff_id FROM vehicle_requests WHERE id = ? AND deleted_at IS NULL");
        $check->execute([$id]);
        $existing = $check->fetch();
        if (!$existing) respond(['status' => 'error', 'message' => 'Request not found']);
        if (!in_array($existing['status'], ['draft', 'returned'], true)) respond(['status' => 'error', 'message' => 'Cannot edit request in ' . $existing['status'] . ' status']);
        if ($existing['staff_id'] !== $userId && !isAdmin()) respond(['status' => 'error', 'message' => 'Unauthorized']);

        $data['updated_at'] = date('Y-m-d H:i:s');
        $sets = implode(', ', array_map(fn($c) => "$c = :$c", array_keys($data)));
        $data['id'] = $id;
        $pdo->prepare("UPDATE vehicle_requests SET $sets WHERE id = :id")->execute($data);
        doAudit($pdo, 'update', "Request #{$id} updated", $id);
        respond(['status' => 'success', 'message' => 'Request updated']);
    } else {
        $data['request_number'] = generateRequestNumber($pdo);
        $data['staff_id'] = $userId;
        $data['status'] = 'draft';
        $cols = implode(', ', array_keys($data));
        $vals = ':' . implode(', :', array_keys($data));
        $pdo->prepare("INSERT INTO vehicle_requests ($cols) VALUES ($vals)")->execute($data);
        $newId = (int)$pdo->lastInsertId();
        doAudit($pdo, 'create', "Request #{$newId} created", $newId);
        respond(['status' => 'success', 'message' => 'Draft saved', 'id' => $newId, 'request_number' => $data['request_number']]);
    }
}

function handleSubmit(PDO $pdo): void
{
    ensureTables($pdo);
    $id = (int)($_POST['id'] ?? 0);
    $userId = (int)($_SESSION['user_id'] ?? 0);

    $stmt = $pdo->prepare("SELECT * FROM vehicle_requests WHERE id = ? AND deleted_at IS NULL");
    $stmt->execute([$id]);
    $request = $stmt->fetch();
    if (!$request) respond(['status' => 'error', 'message' => 'Request not found']);
    if ($request['staff_id'] !== $userId && !isAdmin()) respond(['status' => 'error', 'message' => 'Unauthorized']);
    if (!in_array($request['status'], ['draft', 'returned'], true)) respond(['status' => 'error', 'message' => 'Request already submitted']);

    $pdo->beginTransaction();
    try {
        $pdo->prepare("UPDATE vehicle_requests SET status = 'pending' WHERE id = ?")->execute([$id]);

        // Start workflow using the workflow engine
        workflowStart($pdo, 'vehicle_request', $id, null, $userId);

        $pdo->commit();
        doAudit($pdo, 'submit', "Request #{$id} submitted for approval", $id);
        $firstApprover = $firstStep->fetchColumn();
        if ($firstApprover) {
            notify($pdo, (int)$firstApprover, 'Vehicle Request Pending', 'A vehicle request requires your approval', 'vehicle_request', 'vehicle_request.php', $id);
        }

        respond(['status' => 'success', 'message' => 'Request submitted for approval']);
    } catch (Exception $e) {
        $pdo->rollBack();
        respond(['status' => 'error', 'message' => $e->getMessage()]);
    }
}

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

    $requestId = (int)($_POST['request_id'] ?? 0);
    $userId = (int)($_SESSION['user_id'] ?? 0);
    $comment = trim($_POST['comment'] ?? '');

    $step = getPendingWorkflowStep($pdo, $requestId, $userId);
    if (!$step) respond(['status' => 'error', 'message' => 'No pending step found for you']);

    try {
        $result = workflowApprove($pdo, (int)$step['id'], $userId, $comment);
        doAudit($pdo, 'approve', "Request #{$requestId} step approved", $requestId);
        respond(['status' => 'success', 'message' => $result['status'] === 'workflow_completed' ? 'Request fully approved' : 'Approved']);
    } catch (Exception $e) {
        respond(['status' => 'error', 'message' => $e->getMessage()]);
    }
}

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

    $requestId = (int)($_POST['request_id'] ?? 0);
    $userId = (int)($_SESSION['user_id'] ?? 0);
    $comment = trim($_POST['comment'] ?? '');
    if (!$comment) respond(['status' => 'error', 'message' => 'Comment is required when rejecting']);

    $step = getPendingWorkflowStep($pdo, $requestId, $userId);
    if (!$step) respond(['status' => 'error', 'message' => 'No pending step found for you']);

    try {
        workflowReject($pdo, (int)$step['id'], $userId, $comment);
        doAudit($pdo, 'reject', "Request #{$requestId} rejected", $requestId);
        respond(['status' => 'success', 'message' => 'Request rejected']);
    } catch (Exception $e) {
        respond(['status' => 'error', 'message' => $e->getMessage()]);
    }
}

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

    $requestId = (int)($_POST['request_id'] ?? 0);
    $userId = (int)($_SESSION['user_id'] ?? 0);
    $comment = trim($_POST['comment'] ?? '');
    if (!$comment) respond(['status' => 'error', 'message' => 'Comment is required when returning']);

    $step = getPendingWorkflowStep($pdo, $requestId, $userId);
    if (!$step) respond(['status' => 'error', 'message' => 'No pending step found for you']);

    try {
        workflowReturn($pdo, (int)$step['id'], $userId, $comment);
        doAudit($pdo, 'return', "Request #{$requestId} returned for correction", $requestId);
        respond(['status' => 'success', 'message' => 'Request returned for correction']);
    } catch (Exception $e) {
        respond(['status' => 'error', 'message' => $e->getMessage()]);
    }
}

function getPendingWorkflowStep(PDO $pdo, int $requestId, int $userId): ?array
{
    $stmt = $pdo->prepare("
        SELECT rs.id FROM workflow_run_steps rs
        JOIN workflow_runs r ON r.id = rs.run_id
        WHERE r.module = 'vehicle_request' AND r.record_id = ? AND rs.status = 'pending'
        AND (rs.assigned_user_id = ? OR rs.delegate_user_id = ?)
        ORDER BY rs.step_order LIMIT 1
    ");
    $stmt->execute([$requestId, $userId, $userId]);
    $row = $stmt->fetch();
    return $row ?: null;
}

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

    $id = (int)($_POST['id'] ?? 0);
    $vehicleId = (int)($_POST['vehicle_id'] ?? 0);
    $driverId = !empty($_POST['driver_id']) ? (int)$_POST['driver_id'] : null;

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

    $pdo->beginTransaction();
    try {
        $pdo->prepare("UPDATE vehicle_requests SET assigned_vehicle_id = ?, assigned_driver_id = ?, status = 'assigned' WHERE id = ?")
            ->execute([$vehicleId, $driverId, $id]);

        // Create a trip from the request
        $tripData = [
            'vehicle_id' => $vehicleId,
            'driver_id' => $driverId,
            'start_date' => date('Y-m-d H:i:s'),
            'start_location' => $request['pickup_location'],
            'end_location' => $request['destination'],
            'purpose' => $request['journey_purpose'],
            'status' => 'active',
            'notes' => 'Auto-created from vehicle request #' . $request['request_number'],
            'created_by' => (int)($_SESSION['user_id'] ?? 0),
        ];
        $cols = implode(', ', array_keys($tripData));
        $vals = ':' . implode(', :', array_keys($tripData));
        $pdo->prepare("INSERT INTO trips ($cols) VALUES ($vals)")->execute($tripData);
        $tripId = (int)$pdo->lastInsertId();

        $pdo->commit();
        doAudit($pdo, 'assign', "Request #{$id} assigned to vehicle #{$vehicleId}, trip #{$tripId} created", $id);

        // Notify staff
        notify($pdo, (int)$request['staff_id'], 'Vehicle Assigned', 'A vehicle has been assigned to your request #' . $request['request_number'], 'vehicle_request', 'vehicle_request.php', $id);

        respond(['status' => 'success', 'message' => 'Vehicle assigned and trip created', 'trip_id' => $tripId]);
    } catch (Exception $e) {
        $pdo->rollBack();
        respond(['status' => 'error', 'message' => $e->getMessage()]);
    }
}

function handleDelete(PDO $pdo): void
{
    $id = (int)($_POST['id'] ?? 0);
    if ($id <= 0) respond(['status' => 'error', 'message' => 'Invalid ID']);

    $stmt = $pdo->prepare("SELECT staff_id, status FROM vehicle_requests WHERE id = ? AND deleted_at IS NULL");
    $stmt->execute([$id]);
    $req = $stmt->fetch();
    if (!$req) respond(['status' => 'error', 'message' => 'Not found']);

    $userId = (int)($_SESSION['user_id'] ?? 0);
    if ($req['staff_id'] !== $userId && !isAdmin()) respond(['status' => 'error', 'message' => 'Unauthorized']);
    if (!in_array($req['status'], ['draft', 'returned', 'cancelled'], true)) respond(['status' => 'error', 'message' => 'Cannot delete request in ' . $req['status'] . ' status']);

    softDelete($pdo, 'vehicle_requests', $id);
    doAudit($pdo, 'delete', "Request #{$id} deleted", $id);
    respond(['status' => 'success', 'message' => 'Request deleted']);
}

function handleReference(PDO $pdo): void
{
    ensureTables($pdo);
    $stmt = $pdo->prepare("SELECT id, plate_number AS label FROM vehicles WHERE deleted_at IS NULL AND (vehicle_status IS NULL OR vehicle_status NOT IN ('Retired','Inactive')) ORDER BY plate_number");
    $stmt->execute();
    $vehicles = $stmt->fetchAll();

    $stmt2 = $pdo->prepare("SELECT id, full_name AS label FROM drivers WHERE active_status = 1 AND deleted_at IS NULL ORDER BY full_name");
    $stmt2->execute();
    $drivers = $stmt2->fetchAll();

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