<?php
// public/submit.php

declare(strict_types=1);

$config = require __DIR__ . '/../lib/config.php';
require __DIR__ . '/../lib/db.php';
require __DIR__ . '/../lib/helpers.php';

/* ----------------- PDO error mode ----------------- */

if ($pdo instanceof PDO) {
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}

/* ----------------- Debug logging helper ----------------- */

$logFile = __DIR__ . '/../logs/submit_debug.log';
if (!is_dir(dirname($logFile))) {
    @mkdir(dirname($logFile), 0775, true);
}
function slog(string $msg): void {
    global $logFile;
    @file_put_contents($logFile, '[' . date('c') . '] ' . $msg . "\n", FILE_APPEND);
}

if (($config['env'] ?? 'prod') === 'dev') {
    ini_set('display_errors', '1');
    ini_set('display_startup_errors', '1');
    error_reporting(E_ALL);
}

// Log which DB we’re actually using
try {
    $dbName = $pdo->query('SELECT DATABASE()')->fetchColumn();
    slog('Using database: ' . $dbName);
} catch (Throwable $e) {
    slog('Failed to detect current database: ' . $e->getMessage());
}

slog('--- submit hit start ---');
slog('REQUEST_METHOD=' . ($_SERVER['REQUEST_METHOD'] ?? 'NONE'));
slog('REQUEST_URI=' . ($_SERVER['REQUEST_URI'] ?? ''));
slog('SCRIPT_NAME=' . ($_SERVER['SCRIPT_NAME'] ?? ''));
slog('HTTP_HOST=' . ($_SERVER['HTTP_HOST'] ?? ''));
slog('HTTPS=' . ($_SERVER['HTTPS'] ?? ''));
slog('SERVER_PORT=' . ($_SERVER['SERVER_PORT'] ?? ''));
slog('CONTENT_LENGTH=' . ($_SERVER['CONTENT_LENGTH'] ?? ''));
slog('CONTENT_TYPE=' . ($_SERVER['CONTENT_TYPE'] ?? ''));
slog('HTTP_REFERER=' . ($_SERVER['HTTP_REFERER'] ?? ''));
slog('POST_KEYS=' . json_encode(array_keys($_POST)));
slog('GET_KEYS=' . json_encode(array_keys($_GET)));
slog('RAW_QUERY=' . ($_SERVER['QUERY_STRING'] ?? ''));

/* ----------------- Request method + payload sanity ----------------- */

function parse_size_to_bytes(string $val): int {
    $val = trim($val);
    if ($val === '') return 0;
    $unit = strtolower(substr($val, -1));
    $num  = (int)$val;
    switch ($unit) {
        case 'g': return $num * 1024 * 1024 * 1024;
        case 'm': return $num * 1024 * 1024;
        case 'k': return $num * 1024;
        default:  return (int)$val;
    }
}

// If someone opens /public/submit directly, they’ll GET it (no token).
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
    $t = $_GET['token'] ?? '';
    if (preg_match('/^[a-f0-9-]{36}$/i', $t)) {
        // Optional convenience: if they have a token in the URL, bounce them to the upload page
        // Adjust the upload page path if yours differs.
        header('Location: upload.php?token=' . urlencode($t));
        exit;
    }

    // Otherwise show a friendly message instead of “Invalid token”
    http_response_code(405);
    echo "This endpoint only accepts uploads from the upload form. Please use the link you were sent.";
    exit;
}

// If PHP drops the POST because post_max_size was exceeded, $_POST and $_FILES are empty.
if (empty($_POST) && empty($_FILES)) {
    $contentLen = (int)($_SERVER['CONTENT_LENGTH'] ?? 0);
    $postMax    = parse_size_to_bytes((string)ini_get('post_max_size'));

    if ($postMax > 0 && $contentLen > $postMax) {
        slog("POST dropped: CONTENT_LENGTH={$contentLen} exceeds post_max_size={$postMax}");
        submit_error(
            413,
            "Upload too large for server. Increase post_max_size/upload_max_filesize (and IIS maxAllowedContentLength)."
        );
    }
}


/* ----------------- Helpers ----------------- */

function submit_error(int $code, string $message): void {
    slog("ERROR {$code}: {$message}");
    http_response_code($code);
    echo $message;
    exit;
}

function table_has_column(PDO $pdo, string $table, string $column): bool
{
    static $cache = [];
    $key = $table . '.' . $column;
    if (isset($cache[$key])) {
        return $cache[$key];
    }

    try {
        // Use direct query + quoted value
        $sql = "SHOW COLUMNS FROM `{$table}` LIKE " . $pdo->quote($column);
        $stmt = $pdo->query($sql);
        $cache[$key] = $stmt !== false && (bool)$stmt->fetch();
    } catch (Throwable $e) {
        slog("table_has_column failed for {$table}.{$column}: " . $e->getMessage());
        $cache[$key] = false;
    }

    return $cache[$key];
}


/* ----------------- 1) Validate token & load request ----------------- */

$token = $_POST['token'] ?? '';
slog('Incoming submit: token=' . $token . ' FILE_KEYS=' . json_encode(array_keys($_FILES)));

if (!preg_match('/^[a-f0-9-]{36}$/i', $token)) {
    submit_error(400, 'Invalid token.');
}

$stmt = $pdo->prepare("SELECT * FROM requests WHERE token = ?");
$stmt->execute([$token]);
$req = $stmt->fetch(PDO::FETCH_ASSOC);

if (!$req) {
    submit_error(404, 'Request not found.');
}

$status  = strtolower((string)($req['status'] ?? 'open'));
$expired = is_request_expired($req['expires_at'] ?? null);

slog("Loaded request id={$req['id']} status={$status} expires_at=" . ($req['expires_at'] ?? 'NULL'));

/* --- tenancy from request row, if present --- */
$orgId = array_key_exists('organisation_id', $req) ? (int)$req['organisation_id'] : null;

// Company can be company_id or company, depending on how you named it
if (array_key_exists('company_id', $req)) {
    $companyId = $req['company_id'] !== null ? (int)$req['company_id'] : null;
} elseif (array_key_exists('company', $req)) {
    $companyId = $req['company'] !== null ? (int)$req['company'] : null;
} else {
    $companyId = null;
}

slog("Tenancy on request: orgId=" . var_export($orgId, true) . " companyId=" . var_export($companyId, true));

/* ----------------- 2) Expiry / status checks ----------------- */

if ($status === 'expired' || $expired) {
    slog("Marking as expired and refusing upload.");
    if ($status !== 'expired') {
        $u = $pdo->prepare("UPDATE requests SET status = 'expired' WHERE id = ?");
        $u->execute([(int)$req['id']]);
    }
    submit_error(410, 'This link has expired.');
}

if ($status === 'closed') {
    submit_error(403, 'This request is closed and cannot accept more uploads.');
}

/* ----------------- 3) Ensure upload directory ----------------- */

$uploadRoot = rtrim((string)($config['upload_dir'] ?? ''), '/');
if ($uploadRoot === '') {
    submit_error(500, 'Storage not configured.');
}

$reqDir = $uploadRoot . '/' . $req['token'];

try {
    ensure_dir($reqDir);
} catch (Throwable $e) {
    slog('ensure_dir failed: ' . $e->getMessage());
    submit_error(500, 'Storage error.');
}

slog("Upload dir ready: {$reqDir}");

/* ----------------- 4) Detect extra columns for tenancy + metadata ----------------- */

$notesHasOrg       = table_has_column($pdo, 'request_notes', 'organisation_id');
$notesHasCompanyId = table_has_column($pdo, 'request_notes', 'company_id');
$notesHasCompany   = table_has_column($pdo, 'request_notes', 'company');

$filesHasOrg        = table_has_column($pdo, 'request_files', 'organisation_id');
$filesHasCompanyId  = table_has_column($pdo, 'request_files', 'company_id');
$filesHasCompany    = table_has_column($pdo, 'request_files', 'company');
$filesHasStepCode   = table_has_column($pdo, 'request_files', 'step_code');      // for templates
$filesHasTag        = table_has_column($pdo, 'request_files', 'tag');            // extra meta
$filesHasFileHash   = table_has_column($pdo, 'request_files', 'file_hash');      // extra meta
$filesHasExif       = table_has_column($pdo, 'request_files', 'exif_json');      // extra meta

slog("Notes cols: org=" . (int)$notesHasOrg . " cid=" . (int)$notesHasCompanyId . " c=" . (int)$notesHasCompany);
slog("Files cols: org=" . (int)$filesHasOrg . " cid=" . (int)$filesHasCompanyId . " c=" . (int)$filesHasCompany . " step_code=" . (int)$filesHasStepCode . " tag=" . (int)$filesHasTag . " file_hash=" . (int)$filesHasFileHash . " exif=" . (int)$filesHasExif);

/* ----------------- 5) Save notes (optional) ----------------- */

$notes = trim((string)($_POST['notes'] ?? ''));
if ($notes !== '') {
    try {
        $cols   = ['request_id'];
        $params = [(int)$req['id']];

        if ($notesHasOrg) {
            $cols[]   = 'organisation_id';
            $params[] = $orgId;
        }
        if ($notesHasCompanyId) {
            $cols[]   = 'company_id';
            $params[] = $companyId;
        } elseif ($notesHasCompany) {
            $cols[]   = 'company';
            $params[] = $companyId;
        }

        $cols[]   = 'notes';
        $params[] = $notes;

        $placeholders = implode(',', array_fill(0, count($cols), '?'));
        $sql = "INSERT INTO request_notes (" . implode(',', $cols) . ") VALUES ($placeholders)";

        slog("Inserting note with SQL: {$sql}");
        $s = $pdo->prepare($sql);
        $s->execute($params);
        slog("Note inserted OK.");
    } catch (Throwable $e) {
        slog("Error inserting note: " . $e->getMessage());
        submit_error(500, 'Could not save notes.');
    }
}

/* ----------------- 6) Load template step codes (if template set) ----------------- */

$templateId   = isset($req['template_id']) ? (int)$req['template_id'] : 0;
$stepCodeById = [];

if ($templateId > 0) {
    try {
        $ts = $pdo->prepare("
            SELECT id, code
            FROM towlens_template_steps
            WHERE template_id = ?
        ");
        $ts->execute([$templateId]);
        while ($row = $ts->fetch(PDO::FETCH_ASSOC)) {
            $stepCodeById[(int)$row['id']] = (string)$row['code'];
        }
        slog('Loaded template steps for template_id=' . $templateId . ' count=' . count($stepCodeById));
    } catch (Throwable $e) {
        slog('Failed to load template steps: ' . $e->getMessage());
    }
}

/* ----------------- 7) Validate + save files ----------------- */

$kinds      = ['vehicle', 'scene'];
$max_each   = (int)($config['max_files_each'] ?? 8);
$max_bytes  = (int)($config['max_file_mb'] ?? 22) * 1024 * 1024;
$allowedMap = $config['allowed_mimes'] ?? [];

$anyUploaded = false;

/* ----- 7a) Original vehicle/scene handling (unchanged, but now with tag/file_hash/exif) ----- */

foreach ($kinds as $kind) {
    if (empty($_FILES[$kind])) {
        continue;
    }

    $files = $_FILES[$kind];
    $total = is_array($files['name']) ? count($files['name']) : 0;
    if ($total === 0) {
        continue;
    }

    // Count non-empty
    $count = 0;
    for ($i = 0; $i < $total; $i++) {
        if ((int)$files['error'][$i] === UPLOAD_ERR_NO_FILE) {
            continue;
        }
        $count++;
    }

    slog("Kind {$kind}: total={$total}, non_empty={$count}");

    if ($count > $max_each) {
        submit_error(400, "Too many {$kind} files (max {$max_each}).");
    }

    for ($i = 0; $i < $total; $i++) {
        $err = (int)$files['error'][$i];

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

        if ($err !== UPLOAD_ERR_OK) {
            slog("Upload error for {$kind} index {$i}: code={$err}");
            submit_error(400, "Upload error for {$kind} file.");
        }

        $tmp  = $files['tmp_name'][$i];
        $orig = $files['name'][$i];
        $size = (int)$files['size'][$i];

        if ($size <= 0 || $size > $max_bytes) {
            slog("File too large or empty: {$orig} ({$size} bytes)");
            submit_error(
                400,
                "File too large for {$kind} (max " . (int)($config['max_file_mb'] ?? 22) . "MB)."
            );
        }

        // Detect MIME
        $mime = null;
        if (is_file($tmp)) {
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            if ($finfo) {
                $mime = finfo_file($finfo, $tmp);
                finfo_close($finfo);
            }
        }

        if (!$mime || !isset($allowedMap[$mime])) {
            slog("Unsupported MIME for {$orig}: {$mime}");
            submit_error(400, "Unsupported file type ({$mime}).");
        }

        $ext      = $allowedMap[$mime];
        $safeOrig = sanitize_filename($orig);
        $stored   = $kind . '_' . uniqid('', true) . '.' . $ext;
        $dest     = $reqDir . '/' . $stored;

        if (!@move_uploaded_file($tmp, $dest)) {
            slog("move_uploaded_file failed to {$dest}");
            submit_error(500, "Failed to save {$kind} file.");
        }

        $anyUploaded = true;

        // Build insert for request_files dynamically
        $cols   = ['request_id'];
        $params = [(int)$req['id']];

        if ($filesHasOrg) {
            $cols[]   = 'organisation_id';
            $params[] = $orgId;
        }
        if ($filesHasCompanyId) {
            $cols[]   = 'company_id';
            $params[] = $companyId;
        } elseif ($filesHasCompany) {
            $cols[]   = 'company';
            $params[] = $companyId;
        }

        // No step_code in this mode

        // Extra metadata columns with safe defaults
        if ($filesHasTag) {
            $cols[]   = 'tag';
            $params[] = ''; // empty string default
        }
        if ($filesHasFileHash) {
            $cols[]   = 'file_hash';
            $params[] = ''; // empty string default
        }
        if ($filesHasExif) {
            $cols[]   = 'exif_json';
            $params[] = null; // null ok for text
        }

        $cols = array_merge($cols, ['kind', 'filename', 'original', 'mime', 'size_bytes']);
        $params = array_merge($params, [$kind, $stored, $safeOrig, $mime, $size]);

        $placeholders = implode(',', array_fill(0, count($cols), '?'));
        $sql = "INSERT INTO request_files (" . implode(',', $cols) . ") VALUES ($placeholders)";

        try {
            slog("Inserting file row: {$sql} for {$stored}");
            $ins = $pdo->prepare($sql);
            $ins->execute($params);
        } catch (Throwable $e) {
            slog("Error inserting file metadata for {$stored}: " . $e->getMessage());
            submit_error(500, 'Could not save file metadata.');
        }
    }
}

/* ----- 7b) Template checklist handling: template_files[stepId] ----- */

if (!empty($_FILES['template_files']) && is_array($_FILES['template_files']['name'])) {
    $files   = $_FILES['template_files'];
    $stepIds = array_keys($files['name']);

    slog('Template upload: stepIds=' . json_encode($stepIds));

    // Count non-empty
    $count = 0;
    foreach ($stepIds as $sid) {
        if ((int)$files['error'][$sid] === UPLOAD_ERR_NO_FILE) {
            continue;
        }
        $count++;
    }

    slog("Template non_empty={$count}");

    if ($count > $max_each) {
        submit_error(400, "Too many files (max {$max_each}).");
    }

    foreach ($stepIds as $sid) {
        $err = (int)$files['error'][$sid];

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

        if ($err !== UPLOAD_ERR_OK) {
            slog("Upload error for template step={$sid}: code={$err}");
            submit_error(400, "Upload error for template file.");
        }

        $tmp  = $files['tmp_name'][$sid];
        $orig = $files['name'][$sid];
        $size = (int)$files['size'][$sid];

        if ($size <= 0 || $size > $max_bytes) {
            slog("Template file too large or empty: {$orig} ({$size} bytes)");
            submit_error(
                400,
                "File too large (max " . (int)($config['max_file_mb'] ?? 22) . "MB)."
            );
        }

        // Detect MIME
        $mime = null;
        if (is_file($tmp)) {
            $finfo = finfo_open(FILEINFO_MIME_TYPE);
            if ($finfo) {
                $mime = finfo_file($finfo, $tmp);
                finfo_close($finfo);
            }
        }

        if (!$mime || !isset($allowedMap[$mime])) {
            slog("Unsupported MIME for template {$orig}: {$mime}");
            submit_error(400, "Unsupported file type ({$mime}).");
        }

        $ext      = $allowedMap[$mime];
        $safeOrig = sanitize_filename($orig);
        $stored   = 'template_' . uniqid('', true) . '.' . $ext;
        $dest     = $reqDir . '/' . $stored;

        if (!@move_uploaded_file($tmp, $dest)) {
            slog("move_uploaded_file failed to {$dest} (template)");
            submit_error(500, "Failed to save template file.");
        }

        $anyUploaded = true;

        // Work out step_code
        $stepCode = null;
        if (is_numeric($sid)) {
            $sidInt = (int)$sid;
            if (isset($stepCodeById[$sidInt])) {
                $stepCode = $stepCodeById[$sidInt];
            }
        }

        $kind   = 'vehicle';
        $cols   = ['request_id'];
        $params = [(int)$req['id']];

        if ($filesHasOrg) {
            $cols[]   = 'organisation_id';
            $params[] = $orgId;
        }
        if ($filesHasCompanyId) {
            $cols[]   = 'company_id';
            $params[] = $companyId;
        } elseif ($filesHasCompany) {
            $cols[]   = 'company';
            $params[] = $companyId;
        }

        if ($filesHasStepCode && $stepCode !== null && $stepCode !== '') {
            $cols[]   = 'step_code';
            $params[] = $stepCode;
        }

        // Extra metadata columns with safe defaults
        if ($filesHasTag) {
            $cols[]   = 'tag';
            $params[] = '';
        }
        if ($filesHasFileHash) {
            $cols[]   = 'file_hash';
            $params[] = '';
        }
        if ($filesHasExif) {
            $cols[]   = 'exif_json';
            $params[] = null;
        }

        $cols = array_merge($cols, ['kind', 'filename', 'original', 'mime', 'size_bytes']);
        $params = array_merge($params, [$kind, $stored, $safeOrig, $mime, $size]);

        $placeholders = implode(',', array_fill(0, count($cols), '?'));
        $sql = "INSERT INTO request_files (" . implode(',', $cols) . ") VALUES ($placeholders)";

        try {
            slog("Inserting file row: {$sql} for {$stored} (kind=template, stepCode=" . var_export($stepCode, true) . ")");
            $ins = $pdo->prepare($sql);
            $ins->execute($params);
        } catch (Throwable $e) {
            slog("Error inserting template file metadata for {$stored}: " . $e->getMessage());
            submit_error(500, 'Could not save file metadata (template).');
        }
    }
}

/* ----------------- 8) Update status if any uploads/notes ----------------- */

if ($anyUploaded || $notes !== '') {
    if ($status === 'open') {
        try {
            $u = $pdo->prepare("UPDATE requests SET status = 'submitted' WHERE id = ?");
            $u->execute([(int)$req['id']]);
            slog("Request {$req['id']} marked as submitted.");
        } catch (Throwable $e) {
            slog("Failed to update status to submitted: " . $e->getMessage());
        }
    } else {
        slog("No status change: current status={$status}");
    }
} else {
    slog("No files and no notes submitted; nothing to update.");
}

/* ----------------- 9) Row-count debug + redirect ----------------- */

try {
    $chk = $pdo->prepare("SELECT COUNT(*) FROM request_files WHERE request_id = ?");
    $chk->execute([(int)$req['id']]);
    $count = (int)$chk->fetchColumn();
    slog("Post-insert row count for request_files.request_id={$req['id']}: {$count}");
} catch (Throwable $e) {
    slog("Row-count check failed: " . $e->getMessage());
}

slog("OK -> redirecting to thanks.php");
header('Location: thanks.php');
exit;