<?php
ob_start();
session_start();
require_once '../config/database.php';       // $pdo (Seven Credit DB)
require_once '../config/modules.php';         // company_enabled_modules(), module_on()
require_once '../config/module_access.php';   // mod_can_view(), mod_can_edit()

if (!isset($_SESSION['user_id']) || empty($_SESSION['company_id'])) {
    header('Location: ../auth/login.php'); exit;
}
$companyId   = (int)$_SESSION['company_id'];
$userId      = (int)$_SESSION['user_id'];
$role        = $_SESSION['role'] ?? '';
$companyName = $_SESSION['company_name'] ?? 'Company';

$enabled = company_enabled_modules($pdo, $companyId);
if (!module_on($enabled, 'jobs')) {
    http_response_code(403);
    exit('Job Management is not enabled for your company.');
}
if (!function_exists('esc')) {
    function esc($v){ return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); }
}
if (!function_exists('jobImgUrl')) {
    function jobImgUrl($path) {
        $p = trim((string)$path);
        if ($p === '') return '';
        if (preg_match('#^https?://#i', $p)) return $p;
        if (strpos($p, 'drive_file.php') !== false) return $p;
        if (strpos($p, 'uploads/') === 0) {
            if (is_file(__DIR__ . '/' . $p)) return $p;
            return 'https://7d-lao.com/service-portal/' . $p;
        }
        return $p;
    }
}

$message         = '';
$error           = '';
$success_message = $_SESSION['success_message'] ?? '';
unset($_SESSION['success_message']);

/*
|--------------------------------------------------------------------------
| Pretty job number (JOB-YYYYMMDDHHMMSS -> dd/mm/YYYY (HHMMSS))
|--------------------------------------------------------------------------
*/
function formatJobNo($jobNo)
{
    $jobNo = (string)$jobNo;
    if (preg_match('/JOB-(\d{8})(\d{6})/', $jobNo, $matches)) {
        $dateRaw = $matches[1];
        $time    = $matches[2];
        $dateObj = DateTime::createFromFormat('Ymd', $dateRaw);
        $dateFormatted = $dateObj ? $dateObj->format('d/m/Y') : $dateRaw;
        return $dateFormatted . ' <span class="job-time">(' . $time . ')</span>';
    }
    return $jobNo;
}

function paymentTypeLabel(PDO $pdo, int $companyId, ?string $code): string
{
    $code = (string)$code;
    if ($code === '') {
        return '-';
    }
    try {
        $st = $pdo->prepare("SELECT label FROM sp_payment_types WHERE company_id = ? AND code = ? LIMIT 1");
        $st->execute([$companyId, $code]);
        $label = $st->fetchColumn();
        if ($label !== false && $label !== null && $label !== '') {
            return (string)$label;
        }
    } catch (Throwable $e) { /* fall through */ }
    return ucwords(str_replace('_', ' ', $code));
}

function getUploadErrorMessage(int $errorCode): string
{
    switch ($errorCode) {
        case UPLOAD_ERR_OK:
            return 'Upload successful.';
        case UPLOAD_ERR_INI_SIZE:
            return 'File is too large for server upload limit.';
        case UPLOAD_ERR_FORM_SIZE:
            return 'File is too large for form upload limit.';
        case UPLOAD_ERR_PARTIAL:
            return 'File was only partially uploaded.';
        case UPLOAD_ERR_NO_FILE:
            return 'No file was uploaded.';
        case UPLOAD_ERR_NO_TMP_DIR:
            return 'Server temporary folder is missing.';
        case UPLOAD_ERR_CANT_WRITE:
            return 'Server failed to write file to disk.';
        case UPLOAD_ERR_EXTENSION:
            return 'A PHP extension stopped the upload.';
        default:
            return 'Unknown upload error.';
    }
}

function iniSizeToBytes(string $value): int
{
    $value = trim($value);
    if ($value === '') {
        return 0;
    }

    $last = strtolower(substr($value, -1));
    $number = (float)$value;

    switch ($last) {
        case 'g':
            $number *= 1024;
        case 'm':
            $number *= 1024;
        case 'k':
            $number *= 1024;
            break;
    }

    return (int)$number;
}

/**
 * Human readable file size, e.g. 184 KB / 1.2 MB.
 */
function humanFileSize(int $bytes): string
{
    if ($bytes <= 0) {
        return '0 KB';
    }

    $units = ['B', 'KB', 'MB', 'GB'];
    $i = (int)floor(log($bytes, 1024));
    $i = max(0, min($i, count($units) - 1));

    $size = $bytes / (1024 ** $i);

    if ($i === 0) {
        return (int)$size . ' ' . $units[$i];
    }

    return round($size, $size < 10 ? 1 : 0) . ' ' . $units[$i];
}

/*
|--------------------------------------------------------------------------
| Load the job (scoped by company_id)
|--------------------------------------------------------------------------
*/
$jobId = (int)($_GET['id'] ?? $_POST['id'] ?? 0);
$job   = null;
if ($jobId > 0) {
    $st = $pdo->prepare("SELECT * FROM sp_jobs WHERE id = ? AND company_id = ? LIMIT 1");
    $st->execute([$jobId, $companyId]);
    $job = $st->fetch(PDO::FETCH_ASSOC);
}
if (!$job) {
    http_response_code(404);
    exit('Job not found.');
}

/*
|--------------------------------------------------------------------------
| Access: manager OR the staff assigned to this job may close it.
|--------------------------------------------------------------------------
*/
$canManage  = mod_can_admin($pdo, $companyId, $userId, $role, 'jobs'); // full admin
$canEditOwn = mod_can_edit($pdo, $companyId, $userId, $role, 'jobs');  // staff edit-own or admin

$isAssigned = false;
try {
    $as = $pdo->prepare("SELECT 1 FROM sp_job_staff WHERE company_id = ? AND job_id = ? AND user_id = ? LIMIT 1");
    $as->execute([$companyId, $jobId, $userId]);
    $isAssigned = (bool)$as->fetchColumn();
} catch (Throwable $e) { $isAssigned = false; }

// admins can modify any job; staff can only modify jobs assigned to them
if (!($canManage || ($canEditOwn && $isAssigned))) {
    http_response_code(403);
    exit('You do not have permission to close this job.');
}

/*
|--------------------------------------------------------------------------
| Delete a photo (from the job + Google Drive if applicable)
|--------------------------------------------------------------------------
*/
if (isset($_GET['del_img'])) {
    $imgId = (int)$_GET['del_img'];
    if ($imgId > 0) {
        $g = $pdo->prepare("SELECT image_path FROM sp_job_images WHERE id=? AND company_id=? AND job_id=? LIMIT 1");
        $g->execute([$imgId, $companyId, $jobId]);
        $ipath = (string)($g->fetchColumn() ?: '');
        if ($ipath !== '' && strpos($ipath, 'drive_file.php') !== false) {
            require_once __DIR__ . '/gdrive_lib.php';
            if (function_exists('gd_delete') && function_exists('gd_id_from_path')) {
                $gid = gd_id_from_path($ipath);
                if ($gid !== '') { @gd_delete($gid); }
            }
        }
        $pdo->prepare("DELETE FROM sp_job_images WHERE id=? AND company_id=? AND job_id=?")
            ->execute([$imgId, $companyId, $jobId]);
    }
    header('Location: sp_job_close.php?id=' . $jobId);
    exit;
}

/*
|--------------------------------------------------------------------------
| Load display fields (customer name, machine model) scoped by company_id.
|--------------------------------------------------------------------------
*/
$displayCustomerName = '';
$machineModel        = '';
$machineNo           = (string)($job['machine_no'] ?? '');
try {
    $cs = $pdo->prepare("SELECT customer_name FROM sp_customers WHERE company_id = ? AND sd_no = ? LIMIT 1");
    $cs->execute([$companyId, $job['sd_no'] ?? '']);
    $displayCustomerName = (string)($cs->fetchColumn() ?: '');
} catch (Throwable $e) { $displayCustomerName = ''; }
try {
    $mm = $pdo->prepare("SELECT machine_model FROM sp_machines WHERE company_id = ? AND id = ? LIMIT 1");
    $mm->execute([$companyId, (int)($job['machine_id'] ?? 0)]);
    $machineModel = (string)($mm->fetchColumn() ?: '');
} catch (Throwable $e) { $machineModel = ''; }

/*
|--------------------------------------------------------------------------
| Image conversion helpers  (ported verbatim from close_job.php)
|--------------------------------------------------------------------------
*/
function createImageFromFile(string $sourcePath, int $imageType)
{
    switch ($imageType) {
        case IMAGETYPE_JPEG:
            return @imagecreatefromjpeg($sourcePath);
        case IMAGETYPE_PNG:
            return @imagecreatefrompng($sourcePath);
        case IMAGETYPE_GIF:
            return @imagecreatefromgif($sourcePath);
        case IMAGETYPE_WEBP:
            if (function_exists('imagecreatefromwebp')) {
                return @imagecreatefromwebp($sourcePath);
            }
            return false;
        default:
            return false;
    }
}

function buildResizedCanvas($sourceImage, int $width, int $height, int $maxWidth, int $maxHeight)
{
    $ratio = min($maxWidth / $width, $maxHeight / $height, 1);
    $newWidth = max(1, (int)round($width * $ratio));
    $newHeight = max(1, (int)round($height * $ratio));

    $newImage = imagecreatetruecolor($newWidth, $newHeight);
    if (!$newImage) {
        return false;
    }

    $white = imagecolorallocate($newImage, 255, 255, 255);
    imagefilledrectangle($newImage, 0, 0, $newWidth, $newHeight, $white);

    $copied = imagecopyresampled(
        $newImage,
        $sourceImage,
        0,
        0,
        0,
        0,
        $newWidth,
        $newHeight,
        $width,
        $height
    );

    if (!$copied) {
        imagedestroy($newImage);
        return false;
    }

    return $newImage;
}

/*
|--------------------------------------------------------------------------
| Aggressive "smallest possible" compressors.
|
| Both functions downscale the image, then keep lowering quality until the
| output is under $targetBytes (or until $minQuality is reached). Whatever
| happens, the result is always far smaller than the original upload.
|
| Tune these three knobs if you want even smaller / sharper files:
|   $maxDim      -> max width/height in pixels (lower = smaller file)
|   $targetBytes -> tries to get under this size
|   $minQuality  -> hard floor so images never become unreadable
|--------------------------------------------------------------------------
*/
function saveSmallestWebp(
    string $sourcePath,
    string $destinationPath,
    int $maxDim = 1200,
    int $targetBytes = 180000,
    int $startQuality = 72,
    int $minQuality = 35
): bool {
    if (!function_exists('getimagesize') || !function_exists('imagewebp')) {
        return false;
    }

    $imageInfo = @getimagesize($sourcePath);
    if ($imageInfo === false) {
        return false;
    }

    [$width, $height, $imageType] = $imageInfo;

    $sourceImage = createImageFromFile($sourcePath, $imageType);
    if (!$sourceImage) {
        return false;
    }

    $canvas = buildResizedCanvas($sourceImage, $width, $height, $maxDim, $maxDim);
    imagedestroy($sourceImage);

    if (!$canvas) {
        return false;
    }

    $saved = false;

    for ($quality = $startQuality; $quality >= $minQuality; $quality -= 8) {
        if (!@imagewebp($canvas, $destinationPath, $quality)) {
            $saved = false;
            break;
        }

        $saved = true;
        clearstatcache(true, $destinationPath);

        if ((int)@filesize($destinationPath) <= $targetBytes) {
            break;
        }
    }

    imagedestroy($canvas);

    return $saved;
}

function saveSmallestJpeg(
    string $sourcePath,
    string $destinationPath,
    int $maxDim = 1200,
    int $targetBytes = 180000,
    int $startQuality = 70,
    int $minQuality = 40
): bool {
    if (!function_exists('getimagesize') || !function_exists('imagejpeg')) {
        return false;
    }

    $imageInfo = @getimagesize($sourcePath);
    if ($imageInfo === false) {
        return false;
    }

    [$width, $height, $imageType] = $imageInfo;

    $sourceImage = createImageFromFile($sourcePath, $imageType);
    if (!$sourceImage) {
        return false;
    }

    $canvas = buildResizedCanvas($sourceImage, $width, $height, $maxDim, $maxDim);
    imagedestroy($sourceImage);

    if (!$canvas) {
        return false;
    }

    $saved = false;

    for ($quality = $startQuality; $quality >= $minQuality; $quality -= 8) {
        if (!@imagejpeg($canvas, $destinationPath, $quality)) {
            $saved = false;
            break;
        }

        $saved = true;
        clearstatcache(true, $destinationPath);

        if ((int)@filesize($destinationPath) <= $targetBytes) {
            break;
        }
    }

    imagedestroy($canvas);

    return $saved;
}

/*
|--------------------------------------------------------------------------
| Existing images helper (company-scoped)
|--------------------------------------------------------------------------
*/
function getJobImages(PDO $pdo, int $companyId, int $jobId): array
{
    $stmt = $pdo->prepare("
        SELECT id, image_path, image_type, created_at
        FROM sp_job_images
        WHERE company_id = ? AND job_id = ?
        ORDER BY id DESC
    ");
    $stmt->execute([$companyId, $jobId]);
    return $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
}

/*
|--------------------------------------------------------------------------
| Save images helper (company-scoped + Google Drive upload)
|
| Compresses each upload to the smallest WebP (JPEG fallback) into a temp
| dir, then uploads to Google Drive. Stores the Drive path if the upload
| succeeds, otherwise the local relative path. Mirrors the source's logic.
|--------------------------------------------------------------------------
*/
function saveJobImages(
    PDO $pdo,
    int $companyId,
    int $jobId,
    array $files,
    string &$uploadError = '',
    array &$uploadWarnings = [],
    array &$savedFiles = []
): int {
    $savedCount = 0;

    if (
        !isset($files['name'], $files['tmp_name'], $files['error'], $files['size']) ||
        !is_array($files['name'])
    ) {
        return 0;
    }

    $uploadDirFs = __DIR__ . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR . 'job_images' . DIRECTORY_SEPARATOR;
    $uploadDirDb = 'uploads/job_images/';

    if (!is_dir($uploadDirFs)) {
        if (!mkdir($uploadDirFs, 0775, true) && !is_dir($uploadDirFs)) {
            $uploadError = 'Failed to create upload folder.';
            return 0;
        }
    }

    if (!is_writable($uploadDirFs)) {
        $uploadError = 'Upload folder is not writable: ' . $uploadDirFs;
        return 0;
    }

    $allowedMimeMap = [
        'image/jpeg' => true,
        'image/png'  => true,
        'image/gif'  => true,
        'image/webp' => true,
    ];

    $insertStmt = $pdo->prepare("
        INSERT INTO sp_job_images (company_id, job_id, image_path, image_type, created_at)
        VALUES (?, ?, ?, ?, NOW())
    ");

    foreach ($files['name'] as $index => $originalName) {
        $originalName = trim((string)($originalName ?? ''));
        $displayName = $originalName !== '' ? $originalName : ('File ' . ($index + 1));

        $fileError = (int)($files['error'][$index] ?? UPLOAD_ERR_NO_FILE);

        if ($fileError === UPLOAD_ERR_NO_FILE) {
            continue;
        }

        if ($fileError !== UPLOAD_ERR_OK) {
            $uploadWarnings[] = $displayName . ': ' . getUploadErrorMessage($fileError);
            continue;
        }

        $tmpName = (string)($files['tmp_name'][$index] ?? '');
        $fileSize = (int)($files['size'][$index] ?? 0);

        if ($tmpName === '' || !is_uploaded_file($tmpName)) {
            $uploadWarnings[] = $displayName . ' is not a valid uploaded file.';
            continue;
        }

        if ($fileSize <= 0) {
            $uploadWarnings[] = $displayName . ' is empty.';
            continue;
        }

        if ($fileSize > 50 * 1024 * 1024) {
            $uploadWarnings[] = $displayName . ' is larger than 50MB.';
            continue;
        }

        $imageInfo = @getimagesize($tmpName);
        if ($imageInfo === false) {
            $uploadWarnings[] = $displayName . ' is not a valid image.';
            continue;
        }

        $mimeType = $imageInfo['mime'] ?? '';
        if (!isset($allowedMimeMap[$mimeType])) {
            $uploadWarnings[] = $displayName . ' has unsupported format (' . $mimeType . '). iPhone HEIC photos must be converted to JPG first.';
            continue;
        }

        try {
            $randomPart = bin2hex(random_bytes(4));
        } catch (Throwable $e) {
            $randomPart = (string)mt_rand(1000, 9999);
        }

        $baseFileName = 'job_' . $jobId . '_' . date('Ymd_His') . '_' . $index . '_' . $randomPart;

        $saved = false;
        $dbPath = '';
        $imageType = 'close_job';
        $savedBytes = 0;

        // WebP first -> usually the smallest. Falls back to JPEG if the
        // server has no WebP support.
        $webpFileName = $baseFileName . '.webp';
        $webpFsPath = $uploadDirFs . $webpFileName;
        $webpDbPath = $uploadDirDb . $webpFileName;

        $savedFsPath = '';
        $savedMime   = 'image/jpeg';
        if (saveSmallestWebp($tmpName, $webpFsPath)) {
            $saved = true;
            $dbPath = $webpDbPath;
            $savedFsPath = $webpFsPath;
            $savedMime = 'image/webp';
            clearstatcache(true, $webpFsPath);
            $savedBytes = (int)@filesize($webpFsPath);
        } else {
            $jpgFileName = $baseFileName . '.jpg';
            $jpgFsPath = $uploadDirFs . $jpgFileName;
            $jpgDbPath = $uploadDirDb . $jpgFileName;

            if (saveSmallestJpeg($tmpName, $jpgFsPath)) {
                $saved = true;
                $dbPath = $jpgDbPath;
                $savedFsPath = $jpgFsPath;
                $savedMime = 'image/jpeg';
                clearstatcache(true, $jpgFsPath);
                $savedBytes = (int)@filesize($jpgFsPath);
            }
        }

        if (!$saved) {
            $uploadWarnings[] = $displayName . ' could not be converted and saved on server.';
            continue;
        }

        /* Move the compressed file to Google Drive; keep local only if Drive fails. */
        if (function_exists('gd_upload') && $savedFsPath !== '') {
            try {
                $up = gd_upload(basename($savedFsPath), $savedFsPath, $savedMime);
                if (!empty($up['id'])) {
                    $dbPath = 'drive_file.php?id=' . $up['id'];   // served by the proxy
                    @unlink($savedFsPath);                        // free server disk space
                }
            } catch (Throwable $e) { /* keep the local copy as fallback */ }
        }

        $insertStmt->execute([
            $companyId,
            $jobId,
            $dbPath,
            $imageType
        ]);

        $savedFiles[] = [
            'name'  => $displayName,
            'bytes' => $savedBytes,
        ];

        $savedCount++;
    }

    return $savedCount;
}

/*
|--------------------------------------------------------------------------
| Detect oversized POST request
|--------------------------------------------------------------------------
*/
if ($_SERVER['REQUEST_METHOD'] === 'POST' && empty($_POST) && !empty($_SERVER['CONTENT_LENGTH'])) {
    $postMaxBytes = iniSizeToBytes((string)ini_get('post_max_size'));
    $contentLength = (int)$_SERVER['CONTENT_LENGTH'];

    if ($postMaxBytes > 0 && $contentLength > $postMaxBytes) {
        $error = 'Upload failed: total request is larger than server limit (post_max_size = ' . ini_get('post_max_size') . '). Please upload smaller image(s) or increase PHP upload limits.';
    } else {
        $error = 'Upload failed. The request was empty or too large.';
    }
}

/*
|--------------------------------------------------------------------------
| Submit form (close / modify)
|--------------------------------------------------------------------------
*/
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['close_job'])) {
    $solution_note = trim($_POST['solution_note'] ?? '');

    $staff_fee_raw = isset($_POST['staff_fee']) ? str_replace(',', '', trim($_POST['staff_fee'])) : '';
    $hasStaffFee   = ($staff_fee_raw !== '' && is_numeric($staff_fee_raw));
    $staff_fee     = $hasStaffFee ? (float)$staff_fee_raw : 0;

    $hasUploadedFiles = false;
    if (isset($_FILES['job_images']['error']) && is_array($_FILES['job_images']['error'])) {
        foreach ($_FILES['job_images']['error'] as $fileError) {
            if ((int)$fileError !== UPLOAD_ERR_NO_FILE) {
                $hasUploadedFiles = true;
                break;
            }
        }
    }

    if ($solution_note === '') {
        $error = 'Please enter a solution note.';
    } else {
        // Include gdrive lib only when actually processing an upload/close.
        require_once __DIR__ . '/gdrive_lib.php';

        try {
            $pdo->beginTransaction();

            if ($hasStaffFee) {
                $updateStmt = $pdo->prepare("
                    UPDATE sp_jobs
                    SET solution_note = ?,
                        status        = 'closed',
                        closed_at     = COALESCE(closed_at, NOW()),
                        staff_fee     = ?
                    WHERE id = ? AND company_id = ?
                    LIMIT 1
                ");
                $updateStmt->execute([$solution_note, $staff_fee, $jobId, $companyId]);
            } else {
                $updateStmt = $pdo->prepare("
                    UPDATE sp_jobs
                    SET solution_note = ?,
                        status        = 'closed',
                        closed_at     = COALESCE(closed_at, NOW())
                    WHERE id = ? AND company_id = ?
                    LIMIT 1
                ");
                $updateStmt->execute([$solution_note, $jobId, $companyId]);
            }

            $uploadError = '';
            $uploadWarnings = [];
            $savedFiles = [];
            $savedImageCount = 0;

            if (isset($_FILES['job_images']) && is_array($_FILES['job_images']['name'] ?? null)) {
                @set_time_limit(120);   // give the Drive upload enough time
                $savedImageCount = saveJobImages($pdo, $companyId, $jobId, $_FILES['job_images'], $uploadError, $uploadWarnings, $savedFiles);
            }

            if ($uploadError !== '') {
                throw new Exception($uploadError);
            }

            $pdo->commit();

            $wasClosedAlready = (($job['status'] ?? '') === 'closed');

            $successMessage = 'Job ' . formatJobNoPlain($job['job_no'] ?? $jobId) . ' '
                . ($wasClosedAlready ? 'modified successfully.' : 'closed successfully.');

            if ($savedImageCount > 0) {
                $successMessage .= ' ' . $savedImageCount . ' image(s) uploaded.';

                $sizeBits = [];
                foreach ($savedFiles as $sf) {
                    $sizeBits[] = $sf['name'] . ' -> ' . humanFileSize((int)$sf['bytes']);
                }

                if (!empty($sizeBits)) {
                    $successMessage .= ' Compressed sizes: ' . implode(', ', $sizeBits) . '.';
                }
            }

            if (!empty($uploadWarnings)) {
                $successMessage .= ' Some files were skipped: ' . implode(' ', $uploadWarnings);
            }

            $_SESSION['success_message'] = $successMessage;
            header('Location: sp_job_view.php?id=' . (int)$jobId); exit;

        } catch (Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            $error = 'Failed to update job. ' . $e->getMessage();
        }
    }
}

/*
|--------------------------------------------------------------------------
| Plain (non-HTML) job number for messages.
|--------------------------------------------------------------------------
*/
function formatJobNoPlain($jobNo)
{
    $jobNo = (string)$jobNo;
    if (preg_match('/JOB-(\d{8})(\d{6})/', $jobNo, $m)) {
        $d = DateTime::createFromFormat('Ymd', $m[1]);
        return ($d ? $d->format('d/m/Y') : $m[1]) . ' (' . $m[2] . ')';
    }
    return $jobNo;
}

$existingImages = getJobImages($pdo, $companyId, $jobId);
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Close Job - <?= esc(formatJobNoPlain($job['job_no'] ?? '')) ?></title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Lao:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
    <style>
        * { box-sizing: border-box; }
        body {
            margin: 0;
            font-family: 'Noto Sans Lao', 'Inter', Arial, sans-serif;
            background: #f3f6fb; color: #111827;
            -webkit-font-smoothing: antialiased;
        }
        .sp-topbar {
            background: #0f172a; color: #fff; padding: 14px 22px;
            display: flex; justify-content: space-between; align-items: center;
            flex-wrap: wrap; gap: 10px;
        }
        .sp-topbar .brand { font-size: 18px; font-weight: 700; }
        .sp-topbar .who { color: #93c5fd; font-size: 14px; font-weight: 600; }

        .container { max-width: 900px; margin: 24px auto; padding: 0 16px; }
        .nav-links { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 22px; }
        .nav-links a {
            background: #fff; border: 1px solid #dbe3ee; padding: 10px 14px;
            border-radius: 10px; text-decoration: none; color: #1f2937; font-weight: bold;
        }
        .nav-links a:hover { background: #eff6ff; }
        .nav-links a.active { background: #2563eb; color: #fff; border-color: #2563eb; }

        .back-row { margin-bottom: 16px; }
        .back-btn {
            display: inline-block; background: #2563eb; color: #fff; padding: 8px 14px;
            border-radius: 8px; text-decoration: none; font-weight: bold;
        }
        .back-btn:hover { background: #1d4ed8; }

        .box {
            background: #fff; border-radius: 16px; padding: 22px;
            box-shadow: 0 10px 25px rgba(15, 23, 42, 0.05); margin-bottom: 20px;
        }
        .box h1 { margin-top: 0; font-size: 24px; font-weight: 700; }
        .box h2 { margin-top: 0; font-size: 20px; font-weight: 700; }
        .job-time { font-weight: 500; color: #6b7280; }

        .info-grid { display: grid; grid-template-columns: 180px 1fr; gap: 12px 16px; }
        .label { font-size: 13px; color: #6b7280; font-weight: bold; }
        .value { font-size: 15px; color: #111827; word-break: break-word; }

        .form-group { margin-bottom: 18px; }
        label { display: block; margin-bottom: 8px; font-size: 14px; font-weight: bold; }
        textarea, input[type="file"], input[type="number"] {
            width: 100%; border: 1px solid #d1d5db; border-radius: 12px;
            padding: 12px; font-size: 14px; background: #fff; color: #111827; font-family: inherit;
        }
        textarea { min-height: 140px; resize: vertical; }
        .help { font-size: 12px; color: #6b7280; margin-top: 8px; }

        .btn-row { display: flex; gap: 12px; flex-wrap: wrap; }
        .btn {
            display: inline-block; border: none; border-radius: 10px;
            padding: 12px 18px; cursor: pointer; font-weight: bold; text-decoration: none;
        }
        .btn-primary { background: #0284c7; color: #fff; }
        .btn-primary:hover { background: #0369a1; }
        .btn-secondary { background: #e5e7eb; color: #111827; }
        .btn-secondary:hover { background: #d1d5db; }

        .error   { background: #fee2e2; color: #991b1b; padding: 12px; border-radius: 10px; margin-bottom: 14px; }
        .success { background: #dcfce7; color: #166534; padding: 12px; border-radius: 10px; margin-bottom: 14px; }

        .image-grid {
            display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
            gap: 14px; margin-top: 12px;
        }
        .image-card {
            border: 1px solid #e5e7eb; border-radius: 12px; padding: 10px; background: #fff; position: relative;
        }
        .image-card a { text-decoration: none; }
        .img-del {
            position: absolute; top: 4px; right: 4px; width: 30px; height: 30px;
            background: #dc2626; color: #fff !important; border-radius: 50%;
            display: flex; align-items: center; justify-content: center;
            font-size: 20px; font-weight: 800; line-height: 1; text-decoration: none;
            box-shadow: 0 2px 6px rgba(0,0,0,.3); z-index: 2;
        }
        .img-del:hover { background: #b91c1c; }
        /* lightbox */
        #lightbox { display: none; position: fixed; inset: 0; background: rgba(0,0,0,.88); z-index: 9999; align-items: center; justify-content: center; }
        #lightbox img { max-width: 92%; max-height: 84%; border-radius: 8px; box-shadow: 0 10px 40px rgba(0,0,0,.5); }
        #lightbox .lb-close {
            position: absolute; top: 16px; right: 18px; background: #fff; color: #111827;
            border: none; padding: 10px 18px; border-radius: 10px; font-weight: 800; font-size: 15px; cursor: pointer;
        }
        .image-card img {
            display: block; width: 100%; height: 140px; object-fit: cover;
            border-radius: 10px; background: #f3f4f6; margin-bottom: 8px;
        }
        .image-meta { font-size: 12px; color: #6b7280; margin-bottom: 8px; word-break: break-word; }

        @media (max-width: 700px) {
            .info-grid { grid-template-columns: 1fr; }
            .image-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
        }
    </style>
</head>
<body>
    <div class="sp-topbar">
        <div class="brand">Seven Digital &mdash; Service Portal</div>
        <div class="who">Welcome, <?= esc($companyName) ?></div>
    </div>

    <div class="container">

        <div class="nav-links">
            <?php if ($canManage): ?>
                <a href="sp_jobs_admin.php">Dashboard</a>
                <a href="sp_job_add.php">Open Job</a>
                <a href="../company/staff.php">Staff</a>
                <a href="sp_staff_payments.php">Staff Payment</a>
                <a href="sp_payment_types.php">Payment Types</a>
                <a href="dashboard.php">Back to Company</a>
            <?php else: ?>
                <a href="sp_jobs_staff.php">My Jobs</a>
                <a href="../customer/company_home.php">Back to Home</a>
            <?php endif; ?>
            <a href="../auth/logout.php">Logout</a>
        </div>

        <div class="back-row">
            <a href="<?= $canManage ? 'sp_jobs_admin.php' : 'sp_jobs_staff.php' ?>" class="back-btn">&larr; Back</a>
        </div>

        <?php if ($error): ?>
            <div class="error"><?= esc($error) ?></div>
        <?php endif; ?>

        <?php if ($success_message): ?>
            <div class="success"><?= esc($success_message) ?></div>
        <?php endif; ?>

        <div class="box">
            <h1><?= ($job['status'] ?? '') === 'closed' ? 'Modify Job' : 'Close Job' ?> <?= formatJobNo($job['job_no'] ?? '') ?></h1>

            <div class="info-grid">
                <div class="label">Customer</div>
                <div class="value"><?= esc($displayCustomerName !== '' ? $displayCustomerName : ($job['sd_no'] ?: '-')) ?></div>

                <div class="label">Machine No</div>
                <div class="value"><?= esc($machineNo !== '' ? $machineNo : '-') ?></div>

                <div class="label">Machine Model</div>
                <div class="value"><?= esc($machineModel !== '' ? $machineModel : '-') ?></div>

                <div class="label">Payment Type</div>
                <div class="value"><?= esc(paymentTypeLabel($pdo, $companyId, $job['payment_type'] ?? '')) ?></div>

                <div class="label">Issue</div>
                <div class="value"><?= esc($job['issue_title'] ?? '-') ?></div>

                <div class="label">Current Status</div>
                <div class="value"><?= esc(ucwords(str_replace('_', ' ', $job['status'] ?? '-'))) ?></div>
            </div>
        </div>

        <?php if (!empty($existingImages)): ?>
        <div class="box">
            <h2>Uploaded Pictures</h2>
            <div class="image-grid">
                <?php foreach ($existingImages as $img): ?>
                    <?php $imgUrl = jobImgUrl($img['image_path'] ?? ''); ?>
                    <div class="image-card">
                        <?php if ($imgUrl !== ''): ?>
                            <a href="#" onclick="openLightbox('<?= esc($imgUrl) ?>'); return false;"><img src="<?= esc($imgUrl) ?>" alt="Job image"></a>
                            <a class="img-del" href="sp_job_close.php?del_img=<?= (int)$img['id'] ?>&id=<?= (int)$jobId ?>" onclick="return confirm('Delete this photo? This cannot be undone.')" title="Delete photo">&times;</a>
                        <?php else: ?>
                            <div class="help">Image file missing.</div>
                        <?php endif; ?>
                        <div class="image-meta"><?= esc($img['created_at'] ?? '') ?></div>
                    </div>
                <?php endforeach; ?>
            </div>
        </div>
        <?php endif; ?>

        <div class="box">
            <h2><?= ($job['status'] ?? '') === 'closed' ? 'Modify Closed Job' : 'Solution &amp; Close Job' ?></h2>

            <form method="POST" enctype="multipart/form-data">
                <input type="hidden" name="id" value="<?= (int)$jobId ?>">

                <div class="form-group">
                    <label for="solution_note">Solution Note *</label>
                    <textarea id="solution_note" name="solution_note" required><?= esc($job['solution_note'] ?? '') ?></textarea>
                </div>

                <div class="form-group">
                    <label for="staff_fee">Staff Fee (optional)</label>
                    <input id="staff_fee" type="number" name="staff_fee" min="0" step="0.01"
                           value="<?= esc($job['staff_fee'] ?? '') ?>">
                </div>

                <div class="form-group">
                    <label for="job_images"><?= ($job['status'] ?? '') === 'closed' ? 'Add more photos to this closed job' : 'Upload Pictures' ?></label>
                    <input
                        id="job_images"
                        type="file"
                        name="job_images[]"
                        accept="image/*"
                        multiple
                    >
                    <div class="help">
                        You can upload multiple images of any size &mdash; they are automatically
                        resized and heavily compressed on the server (saved as WebP, usually
                        under 200&nbsp;KB each) and stored in Google Drive. Allowed: JPG, PNG, GIF, WEBP.
                        iPhone HEIC photos are not supported directly. Please convert to JPG or use Most Compatible camera format.
                    </div>
                </div>

                <div class="btn-row">
                    <button type="submit" name="close_job" class="btn btn-primary">
                        <?= ($job['status'] ?? '') === 'closed' ? 'Save Changes' : 'Close Job' ?>
                    </button>

                    <a href="<?= $canManage ? 'sp_jobs_admin.php' : 'sp_jobs_staff.php' ?>" class="btn btn-secondary">Cancel</a>
                </div>
            </form>
        </div>
    </div>

    <!-- Lightbox for viewing a photo full-size -->
    <div id="lightbox" onclick="if(event.target.id==='lightbox')closeLightbox()">
        <button type="button" class="lb-close" onclick="closeLightbox()">&times; Close</button>
        <img id="lightbox-img" src="" alt="Photo">
    </div>
    <script>
        function openLightbox(url){
            document.getElementById('lightbox-img').src = url;
            document.getElementById('lightbox').style.display = 'flex';
            document.body.style.overflow = 'hidden';
        }
        function closeLightbox(){
            document.getElementById('lightbox').style.display = 'none';
            document.getElementById('lightbox-img').src = '';
            document.body.style.overflow = '';
        }
        document.addEventListener('keydown', function(e){ if(e.key==='Escape') closeLightbox(); });
    </script>
</body>
</html>
<?php ob_end_flush(); ?>