<?php
declare(strict_types=1);

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

// ─── Utilities ──────────────────────────────────────────────
function calculateWorkshopDays(?string $startDate, ?string $endDate): ?int
{
    if (!$startDate || !$endDate) {
        return null;
    }
    try {
        $start = new DateTimeImmutable($startDate);
        $end = new DateTimeImmutable($endDate);
    } catch (Throwable) {
        return null;
    }
    if ($end < $start) {
        return null;
    }
    return (int)$start->diff($end)->days + 1;
}

function appendMaintenanceComment(string $existingComment, string $comment, string $actionLabel): string
{
    $existingComment = trim($existingComment);
    $userName = trim((string)($_SESSION['user_name'] ?? 'User'));
    $roleName = trim((string)($_SESSION['role_name'] ?? ''));
    $userDetail = $roleName !== '' ? $userName . ' (' . $roleName . ')' : $userName;
    $entry = sprintf(
        '[%s] %s - %s: %s',
        date('Y-m-d H:i:s'),
        $userDetail,
        $actionLabel,
        trim($comment)
    );
    return $existingComment === '' ? $entry : $existingComment . "\n" . $entry;
}

function generateWorkOrderNumber(PDO $pdo, int $jobId): string
{
    $prefix = 'WO';
    $date = date('Ymd');
    $candidate = sprintf('%s-%s-%04d', $prefix, $date, $jobId);
    $stmt = $pdo->prepare("SELECT COUNT(*) FROM maintenance_jobs WHERE work_order_number = ? AND deleted_at IS NULL");
    $index = 0;
    while (true) {
        $value = $index === 0 ? $candidate : sprintf('%s-%s-%04d', $prefix, $date, $jobId + $index);
        $stmt->execute([$value]);
        if ((int)$stmt->fetchColumn() === 0) {
            return $value;
        }
        $index++;
    }
}

function generateRfqNumber(int $jobId): string
{
    return sprintf('RFQ-%s-%04d', date('Ymd'), $jobId);
}

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

function ensureMaintenanceLineItemsTable(PDO $pdo): void {}

function ensureWorkshopQuoteLineItemsTable(PDO $pdo): void {}

function maintenanceRfqPolicy(): array
{
    return [
        'min_vendors' => 3,
        'require_min_quotes_for_vetting' => true,
        'min_quotes_for_vetting' => 2
    ];
}

function maintenanceStageRoleMatrix(): array
{
    return MAINTENANCE_STAGE_ROLE_MATRIX;
}

function storeQuoteFile(array $file): string
{
    if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
        throw new RuntimeException('Quote file upload failed');
    }
    $ext = validateUploadedFile($file, [
        'application/pdf' => 'pdf',
        'image/jpeg' => 'jpg',
        'image/png' => 'png',
        'image/webp' => 'webp',
        'application/msword' => 'doc',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
        'application/vnd.ms-excel' => 'xls',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
    ], 10 * 1024 * 1024);
    $tmpName = (string)$file['tmp_name'];
    $baseUploadDir = dirname(__DIR__) . '/uploads';
    if (!is_dir($baseUploadDir) && !mkdir($baseUploadDir, 0775, true) && !is_dir($baseUploadDir)) {
        throw new RuntimeException('Unable to initialize uploads directory');
    }
    $preferredDir = $baseUploadDir . '/quotes';
    $fallbackDir = $baseUploadDir . '/quotes_uploads';
    $relativePrefix = 'quotes';
    if (file_exists($preferredDir) && !is_dir($preferredDir)) {
        $uploadDir = $fallbackDir;
        $relativePrefix = 'quotes_uploads';
    } else {
        $uploadDir = $preferredDir;
    }
    if (!is_dir($uploadDir) && !mkdir($uploadDir, 0775, true) && !is_dir($uploadDir)) {
        throw new RuntimeException('Unable to create quote upload directory');
    }
    $filename = randomUploadName('quote', $ext);
    $destination = $uploadDir . '/' . $filename;
    if (!move_uploaded_file($tmpName, $destination)) {
        throw new RuntimeException('Unable to save uploaded quote file');
    }
    return $relativePrefix . '/' . $filename;
}

function normalizeMaintenanceDisplayState(array $job): array
{
    $requisitionStatus = trim((string)($job['requisition_status'] ?? ''));
    $workflowStage = trim((string)($job['workflow_stage'] ?? ''));
    $status = trim((string)($job['status'] ?? ''));

    if (strcasecmp($requisitionStatus, 'Raised') === 0) {
        $job['workflow_stage'] = STAGE_REQUISITION_RAISED;
        $job['status'] = $status !== '' && strcasecmp($status, STATUS_PENDING) !== 0 ? $status : STAGE_REQUISITION_RAISED;
        return $job;
    }
    if (strcasecmp($requisitionStatus, 'Approved') === 0) {
        $job['workflow_stage'] = STAGE_REQUISITION_APPROVED;
        $job['status'] = STAGE_REQUISITION_APPROVED;
        return $job;
    }
    if (strcasecmp($requisitionStatus, 'Rejected') === 0) {
        $job['workflow_stage'] = STAGE_REQUISITION_REJECTED;
        $job['status'] = STAGE_REQUISITION_REJECTED;
        return $job;
    }
    if (in_array(strtolower($requisitionStatus), ['paid', 'payment made'], true)) {
        $job['workflow_stage'] = STAGE_PAYMENT_MADE;
        $job['status'] = STAGE_PAYMENT_MADE;
        return $job;
    }
    if (strcasecmp($requisitionStatus, 'Not Paid') === 0) {
        $job['workflow_stage'] = STAGE_PAYMENT_NOT_MADE;
        $job['status'] = 'Not Paid';
        return $job;
    }
    if ($workflowStage === '' && $status === '') {
        $job['workflow_stage'] = STAGE_COMPLAINT_LOGGED;
        $job['status'] = STATUS_PENDING;
    }
    return $job;
}

// ─── Permission enforcement ────────────────────────────────
function enforceMaintenanceWritePermission(): void
{
    if (!isAdmin() && !can('maintenance.create')) {
        if (!can('maintenance.view')) {
            respond(['status' => 'error', 'message' => 'Unauthorized']);
        }
    }
}

function enforceMaintenanceApprovalPermission(): void
{
    if (!isAdmin() && !can('maintenance.approve') && !can('maintenance.create')) {
        respond(['status' => 'error', 'message' => 'Unauthorized']);
    }
}

// ─── Line items ─────────────────────────────────────────────
function parseMaintenanceLineItems(mixed $raw): array
{
    $decoded = json_decode((string)$raw, true);
    if (!is_array($decoded)) {
        return [];
    }
    $items = [];
    foreach ($decoded as $row) {
        if (!is_array($row)) {
            continue;
        }
        $description = trim((string)($row['description'] ?? ''));
        $cost = normalizeNullableDecimal($row['cost'] ?? null);
        if ($description === '' && $cost === null) {
            continue;
        }
        if ($description === '' || $cost === null || (float)$cost <= 0) {
            respond(['status' => 'error', 'message' => 'Each work line item must include a description and cost greater than 0']);
        }
        $items[] = [
            'description' => $description,
            'cost' => number_format((float)$cost, 2, '.', '')
        ];
    }
    return $items;
}

function calculateMaintenanceLineItemsTotal(array $items): float
{
    $total = 0.0;
    foreach ($items as $item) {
        $total += (float)$item['cost'];
    }
    return $total;
}

function saveMaintenanceLineItems(PDO $pdo, int $jobId, array $items): void
{
    ensureMaintenanceLineItemsTable($pdo);
    $pdo->prepare("UPDATE maintenance_job_line_items SET deleted_at = NOW() WHERE maintenance_job_id = ? AND deleted_at IS NULL")->execute([$jobId]);
    $stmt = $pdo->prepare("
        INSERT INTO maintenance_job_line_items (
            maintenance_job_id,
            item_description,
            estimated_cost,
            sort_order
        ) VALUES (
            :maintenance_job_id,
            :item_description,
            :estimated_cost,
            :sort_order
        )
    ");
    foreach ($items as $index => $item) {
        $stmt->execute([
            'maintenance_job_id' => $jobId,
            'item_description' => $item['description'],
            'estimated_cost' => $item['cost'],
            'sort_order' => $index + 1
        ]);
    }
}

function fetchMaintenanceLineItems(PDO $pdo, int $jobId): array
{
    ensureMaintenanceLineItemsTable($pdo);
    $stmt = $pdo->prepare("
        SELECT
            id,
            id AS source_line_item_id,
            item_description AS description,
            estimated_cost AS cost
        FROM maintenance_job_line_items
        WHERE maintenance_job_id = ?
          AND deleted_at IS NULL
        ORDER BY sort_order ASC, id ASC
    ");
    $stmt->execute([$jobId]);
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

function enforceMaintenanceJobHasLineItems(PDO $pdo, int $jobId, string $message): void
{
    ensureMaintenanceLineItemsTable($pdo);
    $stmt = $pdo->prepare("SELECT COUNT(*) FROM maintenance_job_line_items WHERE maintenance_job_id = ? AND deleted_at IS NULL");
    $stmt->execute([$jobId]);
    if ((int)$stmt->fetchColumn() <= 0) {
        respond(['status' => 'error', 'message' => $message]);
    }
}
