<?php

declare(strict_types=1);

namespace Lab;

final class Helpers
{
    private static string $basePath = '';

    public static function e(?string $value): string
    {
        return htmlspecialchars($value ?? '', ENT_QUOTES, 'UTF-8');
    }

    public static function csrfToken(): string
    {
        if (! isset($_SESSION['csrf_token'])) {
            $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
        }

        return $_SESSION['csrf_token'];
    }

    public static function verifyCsrf(?string $token): bool
    {
        $header = $_SERVER['HTTP_X_CSRF_TOKEN'] ?? null;
        $candidate = is_string($token) && $token !== '' ? $token : (is_string($header) ? $header : null);

        return is_string($candidate)
            && isset($_SESSION['csrf_token'])
            && hash_equals($_SESSION['csrf_token'], $candidate);
    }

    public static function setBasePath(string $basePath): void
    {
        $basePath = '/' . trim(str_replace('\\', '/', $basePath), '/');
        self::$basePath = $basePath === '/' ? '' : $basePath;
    }

    public static function basePath(): string
    {
        return self::$basePath;
    }

    public static function url(string $path = '/'): string
    {
        $base = self::$basePath;
        if ($path === '' || $path === '/') {
            return $base === '' ? '/' : $base . '/';
        }
        if (str_starts_with($path, 'http://') || str_starts_with($path, 'https://')) {
            return $path;
        }
        if (! str_starts_with($path, '/')) {
            $path = '/' . $path;
        }
        if ($base !== '' && ($path === $base || str_starts_with($path, $base . '/'))) {
            return $path;
        }

        return $base . $path;
    }

    public static function requestPath(): string
    {
        $uri = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
        $scriptName = str_replace('\\', '/', (string) ($_SERVER['SCRIPT_NAME'] ?? '/index.php'));
        $base = str_replace('\\', '/', dirname($scriptName));
        if ($base === '/' || $base === '.' || $base === '') {
            $base = '';
        } else {
            $base = rtrim($base, '/');
        }
        if ($base !== '' && ($uri === $base || str_starts_with($uri, $base . '/'))) {
            $uri = substr($uri, strlen($base)) ?: '/';
        }
        $configuredBase = self::basePath();
        if ($configuredBase !== '' && ($uri === $configuredBase || str_starts_with($uri, $configuredBase . '/'))) {
            $uri = substr($uri, strlen($configuredBase)) ?: '/';
        }
        if (str_ends_with($uri, '/index.php')) {
            $uri = substr($uri, 0, -strlen('/index.php')) ?: '/';
        }
        $uri = '/' . trim($uri, '/');

        return $uri === '/' ? '/' : rtrim($uri, '/');
    }

    public static function detectBasePath(string $appRoot): string
    {
        $scriptDir = rtrim(str_replace('\\', '/', dirname((string) ($_SERVER['SCRIPT_NAME'] ?? '/index.php'))), '/');
        if ($scriptDir === '.' || $scriptDir === '/') {
            $scriptDir = '';
        }

        $docRoot = realpath((string) ($_SERVER['DOCUMENT_ROOT'] ?? '')) ?: '';
        $publicDir = realpath($appRoot . '/public') ?: '';
        if ($docRoot !== '' && $publicDir !== '' && $docRoot === $publicDir) {
            return '';
        }
        if (str_ends_with($scriptDir, '/public')) {
            return $scriptDir;
        }
        if ($scriptDir !== '') {
            return $scriptDir;
        }
        if ($docRoot !== '' && $publicDir !== '' && str_starts_with($publicDir, $docRoot)) {
            $relative = substr($publicDir, strlen($docRoot));
            $relative = '/' . trim(str_replace('\\', '/', (string) $relative), '/');

            return $relative === '/' ? '' : $relative;
        }

        return '';
    }

    public static function json(mixed $data, int $status = 200): never
    {
        http_response_code($status);
        header('Content-Type: application/json; charset=UTF-8');
        echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    public static function readJson(): array
    {
        $raw = file_get_contents('php://input') ?: '';
        $data = json_decode($raw, true);

        return is_array($data) ? $data : [];
    }

    public static function euro(int $cents): string
    {
        return number_format($cents / 100, 2, ',', '.') . ' €';
    }

    public static function statusLabel(string $status): string
    {
        return match ($status) {
            'new' => 'Νέα',
            'printing' => 'Σε εκτύπωση',
            'ready' => 'Έτοιμη',
            'delivered' => 'Παραδόθηκε',
            'cancelled' => 'Ακυρώθηκε',
            default => $status,
        };
    }

    public static function channelLabel(string $channel): string
    {
        return match ($channel) {
            'kiosk' => 'Κιόσκ καταστήματος',
            'web' => 'Online',
            default => $channel,
        };
    }

    public static function paperLabel(string $paper): string
    {
        return $paper === 'matte' ? 'Ματ' : 'Γυαλιστερό';
    }

    public static function borderLabel(string $border): string
    {
        return $border === 'white' ? 'Λευκό περιθώριο' : 'Χωρίς περιθώριο';
    }

    public static function paymentLabel(string $payment): string
    {
        return match ($payment) {
            'cash' => 'Μετρητά',
            'card' => 'Κάρτα',
            default => 'Στο ταμείο',
        };
    }

    public static function orderCode(): string
    {
        return 'LAB-' . date('ymd') . '-' . strtoupper(bin2hex(random_bytes(2)));
    }

    /** @param array<string, mixed> $config */
    public static function prepareWritableData(array $config): array
    {
        $override = getenv('LAB_DATA_DIR');
        $preferred = is_string($override) && trim($override) !== ''
            ? rtrim(str_replace('\\', '/', trim($override)), '/')
            : rtrim(str_replace('\\', '/', dirname((string) $config['db_path'])), '/');
        $dir = self::writableDataDir($preferred);
        $oldDir = rtrim(str_replace('\\', '/', dirname((string) $config['db_path'])), '/');
        if ($dir !== $oldDir) {
            self::copyIfMissing($oldDir . '/lab.sqlite', $dir . '/lab.sqlite');
            self::copyIfMissing($oldDir . '/settings.json', $dir . '/settings.json');
        }
        $config['db_path'] = $dir . '/lab.sqlite';
        $config['uploads_dir'] = $dir . '/uploads';
        $config['orders_dir'] = $dir . '/orders';
        $config['hotfolder_dir'] = $dir . '/hotfolder';
        $config['print_dir'] = $dir . '/print-ready';

        return $config;
    }

    private static function writableDataDir(string $preferred): string
    {
        if (self::makeWritableDir($preferred)) {
            return $preferred;
        }
        $local = getenv('LOCALAPPDATA') ?: getenv('APPDATA');
        if (is_string($local) && $local !== '') {
            $fallback = rtrim(str_replace('\\', '/', $local), '/') . '/Panorama Lab/data';
            if (self::makeWritableDir($fallback)) {
                return $fallback;
            }
        }
        $home = getenv('HOME');
        if (PHP_OS_FAMILY === 'Darwin' && is_string($home) && $home !== '') {
            $fallback = rtrim($home, '/') . '/Library/Application Support/Panorama Lab/data';
            if (self::makeWritableDir($fallback)) {
                return $fallback;
            }
        }

        return $preferred;
    }

    /** @param array<string, mixed> $order */
    public static function orderFolderName(array $order): string
    {
        $code = preg_replace('/[^\w.-]+/', '-', (string) ($order['code'] ?? 'order')) ?: 'order';
        $raw = trim((string) ($order['customer_name'] ?? ''));
        $skip = ['', 'Πελάτης κιόσκ', 'Πελάτης', 'Customer', 'Kiosk customer'];
        if (in_array($raw, $skip, true)) {
            return $code;
        }
        $name = preg_replace('/[\/\\\\:*?"<>|]+/u', '-', $raw) ?? '';
        $name = trim((string) preg_replace('/\s+/u', ' ', $name));
        $name = mb_substr($name, 0, 60);
        if ($name === '' || $name === '.' || $name === '..') {
            return $code;
        }

        return $name . '_' . $code;
    }

    private static function makeWritableDir(string $dir): bool
    {
        if (! is_dir($dir)) {
            @mkdir($dir, 0777, true);
        }
        @chmod($dir, 0777);

        return is_dir($dir) && is_writable($dir);
    }

    private static function copyIfMissing(string $from, string $to): void
    {
        if (! is_file($from) || is_file($to)) {
            return;
        }
        @copy($from, $to);
        @chmod($to, 0666);
    }
}
