<?php
// ============================================================
// backend/api/uploads/UploadController.php
// Full diagnostic logging — every step is logged to:
//   backend/logs/upload.log
// ============================================================

class UploadController {

    private string $uploadDir;
    private string $uploadUrl;
    private string $logFile;
    private PDO    $db;

    private array $allowedTypes = [
        'image/jpeg', 'image/jpg', 'image/png',
        'image/gif',  'image/webp',
    ];

    private int $maxSize = 5 * 1024 * 1024; // 5MB

    public function __construct() {
        $this->db = getDB();

        // ----------------------------------------------------------
        // Set up log file at backend/logs/upload.log
        // ----------------------------------------------------------
        $logsDir       = realpath(__DIR__ . '/../../') . DIRECTORY_SEPARATOR . 'logs';
        $this->logFile = $logsDir . DIRECTORY_SEPARATOR . 'upload.log';

        if (!is_dir($logsDir)) {
            mkdir($logsDir, 0755, true);
        }

        // ----------------------------------------------------------
        // Resolve uploads directory
        // __DIR__ = .../backend/api/uploads
        // realpath(__DIR__ . '/../../') = .../backend
        // ----------------------------------------------------------
        $backendDir      = realpath(__DIR__ . '/../../');
        $this->uploadDir = $backendDir . DIRECTORY_SEPARATOR . 'uploads' . DIRECTORY_SEPARATOR;

        // ----------------------------------------------------------
        // Build public URL
        // ----------------------------------------------------------
        $scheme          = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
        $host            = $_SERVER['HTTP_HOST'];
        $baseUrl         = $scheme . '://' . $host
                         . str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME']));
        $this->uploadUrl = rtrim($baseUrl, '/') . '/uploads/';

        // ----------------------------------------------------------
        // Ensure uploads folder exists and is writable
        // ----------------------------------------------------------
        if (!is_dir($this->uploadDir)) {
            $this->log('WARN', 'uploads/ folder missing — attempting to create: ' . $this->uploadDir);
            if (!mkdir($this->uploadDir, 0755, true)) {
                $this->log('FATAL', 'Could not create uploads dir: ' . $this->uploadDir);
                sendError('Could not create uploads directory: ' . $this->uploadDir, 500);
            }
            $this->log('INFO', 'uploads/ folder created successfully');
        }

        if (!is_writable($this->uploadDir)) {
            $this->log('FATAL', 'uploads/ folder is NOT writable: ' . $this->uploadDir);
            sendError('Uploads folder is not writable: ' . $this->uploadDir, 500);
        }

        $this->log('INFO', '=== UploadController initialised ===');
        $this->log('INFO', 'uploadDir  : ' . $this->uploadDir);
        $this->log('INFO', 'uploadUrl  : ' . $this->uploadUrl);
        $this->log('INFO', 'dir exists : ' . (is_dir($this->uploadDir)   ? 'YES' : 'NO'));
        $this->log('INFO', 'writable   : ' . (is_writable($this->uploadDir) ? 'YES' : 'NO'));
    }

    // ----------------------------------------------------------
    // POST /uploads
    // ----------------------------------------------------------
    public function upload(): void {
        $this->log('INFO', '--- POST /uploads called ---');
        $this->log('INFO', 'Request method : ' . $_SERVER['REQUEST_METHOD']);
        $this->log('INFO', 'Content-Type   : ' . ($_SERVER['CONTENT_TYPE'] ?? 'not set'));

        $admin = requireAdmin();
        $this->log('INFO', 'Admin user     : id=' . $admin['id'] . ' email=' . $admin['email']);

        // ----------------------------------------------------------
        // Step 1 — Check $_FILES
        // ----------------------------------------------------------
        $this->log('INFO', 'STEP 1: Checking $_FILES ...');
        $this->log('INFO', '$_FILES contents: ' . json_encode($_FILES));

        if (!isset($_FILES['image'])) {
            $this->log('ERROR', '$_FILES["image"] not set. Keys present: ' . implode(', ', array_keys($_FILES)));
            sendError(
                'No file received. Field name must be "image". ' .
                'Do NOT set Content-Type header manually in fetch() when sending FormData.',
                422
            );
        }

        $file     = $_FILES['image'];
        $tmpPath  = $file['tmp_name'];
        $origName = $file['name'];
        $size     = (int)$file['size'];
        $error    = (int)$file['error'];

        $this->log('INFO', 'File name      : ' . $origName);
        $this->log('INFO', 'File size      : ' . $size . ' bytes (' . $this->humanSize($size) . ')');
        $this->log('INFO', 'Tmp path       : ' . $tmpPath);
        $this->log('INFO', 'PHP error code : ' . $error);
        $this->log('INFO', 'Tmp file exists: ' . (file_exists($tmpPath) ? 'YES' : 'NO'));

        // ----------------------------------------------------------
        // Step 2 — PHP error code
        // ----------------------------------------------------------
        $this->log('INFO', 'STEP 2: Checking PHP upload error code ...');
        if ($error !== UPLOAD_ERR_OK) {
            $msgs = [
                UPLOAD_ERR_INI_SIZE   => 'File too large — increase upload_max_filesize in php.ini (current: ' . ini_get('upload_max_filesize') . ')',
                UPLOAD_ERR_FORM_SIZE  => 'File exceeds form MAX_FILE_SIZE',
                UPLOAD_ERR_PARTIAL    => 'File only partially uploaded',
                UPLOAD_ERR_NO_FILE    => 'No file was sent',
                UPLOAD_ERR_NO_TMP_DIR => 'PHP temp dir missing — check upload_tmp_dir in php.ini',
                UPLOAD_ERR_CANT_WRITE => 'PHP cannot write to temp dir: ' . sys_get_temp_dir(),
                UPLOAD_ERR_EXTENSION  => 'A PHP extension blocked the upload',
            ];
            $msg = $msgs[$error] ?? 'Unknown PHP error code: ' . $error;
            $this->log('ERROR', 'PHP upload error: ' . $msg);
            sendError($msg, 400);
        }
        $this->log('INFO', 'PHP error code OK (UPLOAD_ERR_OK)');

        // ----------------------------------------------------------
        // Step 3 — Size
        // ----------------------------------------------------------
        $this->log('INFO', 'STEP 3: Checking file size ...');
        if ($size === 0) {
            $this->log('ERROR', 'File is 0 bytes');
            sendError('Uploaded file is empty (0 bytes)', 422);
        }
        if ($size > $this->maxSize) {
            $this->log('ERROR', 'File too large: ' . $this->humanSize($size) . ' > 5MB');
            sendError('File too large (' . $this->humanSize($size) . '). Max is 5MB.', 413);
        }
        $this->log('INFO', 'Size OK: ' . $this->humanSize($size));

        // ----------------------------------------------------------
        // Step 4 — MIME type
        // ----------------------------------------------------------
        $this->log('INFO', 'STEP 4: Detecting real MIME type via fileinfo ...');
        if (!function_exists('finfo_open')) {
            $this->log('ERROR', 'fileinfo extension not loaded');
            sendError('PHP fileinfo extension not enabled. Add extension=fileinfo in php.ini', 500);
        }

        $finfo    = finfo_open(FILEINFO_MIME_TYPE);
        $mimeType = finfo_file($finfo, $tmpPath);
        finfo_close($finfo);

        $this->log('INFO', 'Detected MIME  : ' . $mimeType);
        $this->log('INFO', 'Browser MIME   : ' . $file['type']);

        if (!in_array($mimeType, $this->allowedTypes, true)) {
            $this->log('ERROR', 'MIME type not allowed: ' . $mimeType);
            sendError('Invalid file type: "' . $mimeType . '". Only JPG, PNG, GIF, WebP allowed.', 415);
        }
        $this->log('INFO', 'MIME type OK');

        // ----------------------------------------------------------
        // Step 5 — Build filename and destination path
        // ----------------------------------------------------------
        $this->log('INFO', 'STEP 5: Building filename ...');
        $ext      = $this->mimeToExt($mimeType);
        $base     = pathinfo($origName, PATHINFO_FILENAME);
        $base     = strtolower(preg_replace('/[^a-zA-Z0-9_\-]/', '_', $base));
        $base     = substr($base, 0, 40);
        $filename = $base . '_' . uniqid() . '.' . $ext;
        $destPath = $this->uploadDir . $filename;
        $fullUrl  = $this->uploadUrl . $filename;

        $this->log('INFO', 'Filename       : ' . $filename);
        $this->log('INFO', 'Dest path      : ' . $destPath);
        $this->log('INFO', 'Public URL     : ' . $fullUrl);

        // ----------------------------------------------------------
        // Step 6 — move_uploaded_file()
        // ----------------------------------------------------------
        $this->log('INFO', 'STEP 6: Calling move_uploaded_file() ...');
        $moved = move_uploaded_file($tmpPath, $destPath);
        $this->log('INFO', 'move_uploaded_file result: ' . ($moved ? 'TRUE' : 'FALSE'));

        if (!$moved) {
            $this->log('ERROR', 'move_uploaded_file() failed!');
            $this->log('ERROR', 'Dest dir exists  : ' . (is_dir($this->uploadDir)      ? 'YES' : 'NO'));
            $this->log('ERROR', 'Dest dir writable: ' . (is_writable($this->uploadDir) ? 'YES' : 'NO'));
            $this->log('ERROR', 'Tmp file exists  : ' . (file_exists($tmpPath)         ? 'YES' : 'NO'));
            $this->log('ERROR', 'PHP safe_mode    : ' . ini_get('safe_mode'));
            $this->log('ERROR', 'open_basedir     : ' . ini_get('open_basedir'));

            sendError(
                'move_uploaded_file() failed. ' .
                'Dest: ' . $destPath . ' | ' .
                'Dir writable: ' . (is_writable($this->uploadDir) ? 'YES' : 'NO') . ' | ' .
                'Check backend/logs/upload.log for full details.',
                500
            );
        }

        // ----------------------------------------------------------
        // Step 7 — Verify file is on disk
        // ----------------------------------------------------------
        $this->log('INFO', 'STEP 7: Verifying file on disk ...');
        $fileOnDisk = file_exists($destPath);
        $diskSize   = $fileOnDisk ? filesize($destPath) : 0;
        $this->log('INFO', 'File on disk   : ' . ($fileOnDisk ? 'YES' : 'NO'));
        $this->log('INFO', 'Size on disk   : ' . $diskSize . ' bytes');

        if (!$fileOnDisk) {
            $this->log('ERROR', 'File not found after move! Dest: ' . $destPath);
            sendError('File not found after move to: ' . $destPath, 500);
        }

        // ----------------------------------------------------------
        // Step 8 — Insert into media table
        // ----------------------------------------------------------
        $this->log('INFO', 'STEP 8: Inserting into media table ...');
        try {
            $stmt = $this->db->prepare(
                'INSERT INTO media (uploaded_by, filename, original_name, url, file_size, mime_type)
                 VALUES (?, ?, ?, ?, ?, ?)'
            );
            $result = $stmt->execute([
                (int)$admin['id'],
                $filename,
                $origName,
                $fullUrl,
                $size,
                $mimeType,
            ]);
            $mediaId = (int)$this->db->lastInsertId();
            $this->log('INFO', 'DB insert result: ' . ($result ? 'SUCCESS' : 'FAILED'));
            $this->log('INFO', 'New media ID    : ' . $mediaId);
        } catch (PDOException $e) {
            $this->log('ERROR', 'PDOException during DB insert: ' . $e->getMessage());
            $this->log('ERROR', 'SQL state: ' . $e->getCode());
            @unlink($destPath); // rollback disk write
            sendError(
                'DB insert failed: ' . $e->getMessage() .
                ' | Make sure the media table exists in your database.',
                500
            );
        }

        // ----------------------------------------------------------
        // Step 9 — Done
        // ----------------------------------------------------------
        $this->log('INFO', 'STEP 9: Upload complete. media_id=' . $mediaId . ' file=' . $filename);
        $this->log('INFO', '--- Upload finished successfully ---');

        sendSuccess([
            'id'            => $mediaId,
            'filename'      => $filename,
            'original_name' => $origName,
            'url'           => $fullUrl,
            'file_size'     => $size,
            'size_label'    => $this->humanSize($size),
            'mime_type'     => $mimeType,
            'uploaded_by'   => (int)$admin['id'],
            'uploader_name' => $admin['name'] ?? 'Admin',
            'created_at'    => date('Y-m-d H:i:s'),
        ], 'Image uploaded successfully', 201);
    }

    // ----------------------------------------------------------
    // GET /uploads
    // ----------------------------------------------------------
    public function index(): void {
        requireAdmin();
        $this->log('INFO', '--- GET /uploads called ---');

        $stmt = $this->db->prepare(
            'SELECT m.*, u.name AS uploader_name
             FROM media m
             JOIN users u ON u.id = m.uploaded_by
             ORDER BY m.created_at DESC'
        );
        $stmt->execute();
        $rows = $stmt->fetchAll();

        $this->log('INFO', 'Rows returned from media table: ' . count($rows));

        foreach ($rows as &$row) {
            $row['size_label'] = $this->humanSize((int)$row['file_size']);
        }

        sendSuccess($rows);
    }

    // ----------------------------------------------------------
    // GET /uploads/{id}
    // ----------------------------------------------------------
    public function show(string $id): void {
        requireAdmin();
        $stmt = $this->db->prepare(
            'SELECT m.*, u.name AS uploader_name
             FROM media m JOIN users u ON u.id = m.uploaded_by
             WHERE m.id = ?'
        );
        $stmt->execute([$id]);
        $row = $stmt->fetch();
        if (!$row) sendError('Image not found', 404);
        $row['size_label'] = $this->humanSize((int)$row['file_size']);
        sendSuccess($row);
    }

    // ----------------------------------------------------------
    // DELETE /uploads/{filename}
    // ----------------------------------------------------------
    public function delete(string $filename): void {
        requireAdmin();
        // $this->log('INFO', '--- DELETE /uploads/' . $filename . ' ---');

        $filename = basename($filename);
        if (!preg_match('/^[a-zA-Z0-9_\-]+\.(jpg|jpeg|png|gif|webp)$/i', $filename)) {
            sendError('Invalid filename', 400);
        }

        $stmt = $this->db->prepare('SELECT * FROM media WHERE filename = ?');
        $stmt->execute([$filename]);
        $media = $stmt->fetch();
        if (!$media) sendError('Image not found in database', 404);

        $path = $this->uploadDir . $filename;
        $diskDeleted = false;
        if (file_exists($path)) {
            $diskDeleted = unlink($path);
            $this->log('INFO', 'Disk delete: ' . ($diskDeleted ? 'OK' : 'FAILED') . ' path=' . $path);
        } else {
            $this->log('WARN', 'File not found on disk, removing DB record only: ' . $path);
        }

        $this->db->prepare('DELETE FROM media WHERE filename = ?')->execute([$filename]);
        $this->log('INFO', 'DB record deleted for: ' . $filename);

        sendSuccess([], 'Image deleted');
    }

    // ----------------------------------------------------------
    // Logger — writes to backend/logs/upload.log
    // ----------------------------------------------------------
    private function log(string $level, string $message): void {
        $line = '[' . date('Y-m-d H:i:s') . '] [' . strtoupper($level) . '] ' . $message . PHP_EOL;

        // Write to our log file
        file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);

        // Also send to PHP's error_log (shows in WAMP's Apache error log)
        error_log('[HealthVibe Upload] ' . $level . ': ' . $message);
    }

    // ----------------------------------------------------------
    // Helpers
    // ----------------------------------------------------------
    private function mimeToExt(string $mime): string {
        return [
            'image/jpeg' => 'jpg',
            'image/jpg'  => 'jpg',
            'image/png'  => 'png',
            'image/gif'  => 'gif',
            'image/webp' => 'webp',
        ][$mime] ?? 'jpg';
    }

    private function humanSize(int $bytes): string {
        if ($bytes >= 1048576) return round($bytes / 1048576, 1) . ' MB';
        if ($bytes >= 1024)    return round($bytes / 1024, 1) . ' KB';
        return $bytes . ' B';
    }
}
