<?php
use App\Core\Config;

if (!function_exists('base_url')) {
    function base_url(string $path = ''): string {
        $base = rtrim(Config::baseUrl(), '/');

        // Déterminer le sous-répertoire (ex: "/public") depuis le SCRIPT_NAME ou le chemin de requête
        $script = $_SERVER['SCRIPT_NAME'] ?? '';
        // Si l'app tourne sous /public/... (routes), on veut la racine jusqu'à /public (pas /public/cartographie)
        if (preg_match('#^(.*?/public)(?:/.*)?$#', (string)$script, $m)) {
            $scriptDir = rtrim((string)$m[1], '/');
        } else {
            $scriptDir = rtrim(dirname((string)$script), '/');
        }
        if ($scriptDir === '/' || $scriptDir === '\\' || $scriptDir === '.') { $scriptDir = ''; }
        // Si on passe par un "shim" public/cartographie(/...) ou public/cartography(/...),
        // on veut une base au niveau de /public, pas /public/cartographie.
        if ($scriptDir !== '') {
            $scriptDir = preg_replace('~/(cartography|cartographie)(/.*)?$~', '', (string)$scriptDir);
            $scriptDir = rtrim((string)$scriptDir, '/');
        }
        // Ne pas tenter de déduire un préfixe depuis REQUEST_URI : dans une app routée (mod_rewrite),
        // le 1er segment est une route (/cartographie, /incidents, ...), pas un basePath.

        if ($base === '') {
            // Fallback dynamique si app_url n'est pas configurée
            $fallback = $scriptDir;
            $base = $fallback ?: '';
        } else {
            // Si app_url est défini mais sans chemin et que l'app tourne dans un sous-dossier,
            // on s'assure d'inclure ce sous-dossier (ex: ajouter "/public" si manquant)
            $basePath = parse_url($base, PHP_URL_PATH) ?: '';
            if ($scriptDir && ($basePath === '' || $basePath === '/')) {
                $base .= $scriptDir;
            }
        }
        $path = '/' . ltrim($path, '/');
        return $base . $path;
    }
}

if (!function_exists('asset')) {
    function asset(string $path): string {
        return base_url($path);
    }
}

if (!function_exists('storage_path')) {
    function storage_path(string $sub = ''): string {
        return App\Core\Config::storagePath($sub);
    }
}

if (!function_exists('upload_url')) {
    function upload_url(string $path = ''): string {
        return base_url('storage/uploads/' . ltrim($path, '/'));
    }
}

if (!function_exists('upload_public_path')) {
    function upload_public_path(string $path = ''): string {
        return dirname(__DIR__) . '/public/storage/uploads/' . ltrim(str_replace('\\', '/', $path), '/');
    }
}

if (!function_exists('ftth_upload_url')) {
    function ftth_upload_url($ticketId, ?string $path): string {
        $normalized = trim((string)$path);
        if ($normalized === '') {
            return '';
        }

        if (preg_match('~^https?://~i', $normalized)) {
            return $normalized;
        }

        $normalized = ltrim(str_replace('\\', '/', $normalized), '/');
        if (str_starts_with($normalized, 'public/')) {
            $normalized = substr($normalized, 7);
        }
        if (str_starts_with($normalized, 'storage/uploads/')) {
            $normalized = substr($normalized, strlen('storage/uploads/'));
        }

        $ticketPrefix = 'ftth/' . (int)$ticketId . '/';
        if (!str_starts_with($normalized, $ticketPrefix)) {
            $normalized = $ticketPrefix . basename($normalized);
        }

        return upload_url($normalized);
    }
}

if (!function_exists('normalize_assigned_city_value')) {
    function normalize_assigned_city_value($value): ?string {
        $normalized = trim((string)$value);
        if ($normalized === '') {
            return null;
        }

        $normalized = function_exists('mb_strtolower')
            ? mb_strtolower($normalized, 'UTF-8')
            : strtolower($normalized);

        return $normalized;
    }
}

if (!function_exists('assigned_city_sql_condition')) {
    function assigned_city_sql_condition(string $alias = 't'): string {
        $prefix = trim($alias);
        if ($prefix !== '' && !str_ends_with($prefix, '.')) {
            $prefix .= '.';
        }

        $extraFieldsExpr = "CASE WHEN JSON_VALID(COALESCE({$prefix}extra_fields, '')) THEN COALESCE({$prefix}extra_fields, '{}') ELSE '{}' END";

        return "LOWER(TRIM(COALESCE("
            . "NULLIF(JSON_UNQUOTE(JSON_EXTRACT({$extraFieldsExpr}, '$.Ville')), ''), "
            . "NULLIF(JSON_UNQUOTE(JSON_EXTRACT({$extraFieldsExpr}, '$.Commune')), ''), "
            . "NULLIF(TRIM(COALESCE({$prefix}site_name, '')), ''), "
            . "NULLIF(TRIM(COALESCE({$prefix}client_address, '')), '')"
            . "))) = ?";
    }
}

if (!function_exists('assigned_city_sql_params')) {
    function assigned_city_sql_params(string $assignedCity): array {
        return [normalize_assigned_city_value($assignedCity)];
    }
}

if (!function_exists('route_url')) {
    /**
     * Génère une URL de route compatible avec le Router
     * Config::baseUrl() inclut déjà le basePath (/public), pas besoin de l'ajouter
     */
    function route_url(string $path, array $params = []): string {
        // Config::baseUrl() retourne déjà https://domain.com/public
        $baseUrl = rtrim(Config::baseUrl(), '/');
        
        // Si pas de config baseUrl, construire depuis les variables serveur
        if (!$baseUrl) {
            $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
            $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
            $scriptName = $_SERVER['SCRIPT_NAME'] ?? '/index.php';
            $basePath = dirname($scriptName);
            
            // Normaliser le basePath
            if ($basePath === '/' || $basePath === '\\' || $basePath === '.') {
                $basePath = '';
            }
            
            $baseUrl = $scheme . '://' . $host . $basePath;
        }
        
        // Construire l'URL finale (baseUrl contient déjà /public)
        $path = '/' . ltrim($path, '/');
        $url = $baseUrl . $path;
        
        // Ajouter les paramètres GET si présents
        if (!empty($params)) {
            $url .= (str_contains($url, '?') ? '&' : '?') . http_build_query($params);
        }
        
        return $url;
    }
}

if (!function_exists('format_datetime')) {
    /**
     * Formate une date/heure selon le fuseau horaire configuré
     * @param string|int|null $datetime Date/heure à formater (timestamp, string SQL, etc.)
     * @param string $format Format d'affichage (défaut: d/m/Y H:i)
     * @param bool $withTimezone Afficher le fuseau horaire dans le résultat
     * @return string Date formatée ou chaîne vide si invalide
     */
    function format_datetime($datetime, string $format = 'd/m/Y H:i', bool $withTimezone = false): string {
        if (empty($datetime)) return '';
        
        try {
            $timezone = Config::get('timezone', 'Africa/Abidjan');
            $tz = new DateTimeZone($timezone);
            
            // Créer DateTime depuis différents formats
            if (is_numeric($datetime)) {
                $dt = new DateTime('@' . $datetime);
                $dt->setTimezone($tz);
            } else {
                $dt = new DateTime($datetime);
                $dt->setTimezone($tz);
            }
            
            $result = $dt->format($format);
            
            if ($withTimezone) {
                $result .= ' (' . $dt->format('T') . ')';
            }
            
            return $result;
        } catch (Exception $e) {
            return '';
        }
    }
}

if (!function_exists('format_date')) {
    /**
     * Formate une date (sans l'heure) selon le fuseau horaire configuré
     * @param string|int|null $date Date à formater
     * @return string Date formatée (d/m/Y) ou chaîne vide
     */
    function format_date($date): string {
        return format_datetime($date, 'd/m/Y', false);
    }
}

if (!function_exists('format_time')) {
    /**
     * Formate une heure selon le fuseau horaire configuré
     * @param string|int|null $time Heure à formater
     * @return string Heure formatée (H:i) ou chaîne vide
     */
    function format_time($time): string {
        return format_datetime($time, 'H:i', false);
    }
}

if (!function_exists('relative_time')) {
    /**
     * Retourne le temps relatif (ex: "il y a 2 heures")
     * @param string|int|null $datetime Date/heure
     * @return string Temps relatif en français
     */
    function relative_time($datetime): string {
        if (empty($datetime)) return '';
        
        try {
            $timezone = Config::get('timezone', 'Africa/Abidjan');
            $tz = new DateTimeZone($timezone);
            
            if (is_numeric($datetime)) {
                $dt = new DateTime('@' . $datetime);
                $dt->setTimezone($tz);
            } else {
                $dt = new DateTime($datetime);
                $dt->setTimezone($tz);
            }
            
            $now = new DateTime('now', $tz);
            $diff = $now->getTimestamp() - $dt->getTimestamp();
            
            if ($diff < 0) {
                $diff = abs($diff);
                $prefix = 'dans ';
            } else {
                $prefix = 'il y a ';
            }
            
            if ($diff < 60) return $prefix . $diff . ' secondes';
            if ($diff < 3600) return $prefix . floor($diff / 60) . ' minutes';
            if ($diff < 86400) return $prefix . floor($diff / 3600) . ' heures';
            if ($diff < 604800) return $prefix . floor($diff / 86400) . ' jours';
            if ($diff < 2592000) return $prefix . floor($diff / 604800) . ' semaines';
            if ($diff < 31536000) return $prefix . floor($diff / 2592000) . ' mois';
            return $prefix . floor($diff / 31536000) . ' ans';
        } catch (Exception $e) {
            return '';
        }
    }
}

if (!function_exists('incident_status_color')) {
    /**
     * Détermine la couleur d'affichage du statut incident en appliquant
     * les règles métier (SLA respecté / non respecté, Nouveau, En cours, Traité, etc.).
     */
    function incident_status_color(array $incident): string {
        $statusKey = strtolower($incident['status_key'] ?? '');
        $statusLabelLow = strtolower($incident['status_label'] ?? '');
        $baseColor = trim((string)($incident['status_color'] ?? ''));

        // Données SLA (si disponibles sur l'incident)
        $expRes = !empty($incident['expected_resolution_at'] ?? null)
            ? strtotime((string)$incident['expected_resolution_at'])
            : null;
        $resAt = !empty($incident['resolved_at'] ?? null)
            ? strtotime((string)$incident['resolved_at'])
            : null;

        // Détection d'un incident considéré comme "clôturé"
        $isClosed = in_array($statusKey, ['closed','resolu','resolved','cloture','clôturé','resolved_temp','resolved_final'], true)
            || str_contains($statusLabelLow, 'clôtur')
            || str_contains($statusLabelLow, 'clos')
            || str_contains($statusLabelLow, 'résolu');

        // État SLA pour les incidents clôturés
        $slaKnown = false;
        $slaBreached = false;
        if ($expRes && $resAt) {
            $slaKnown = true;
            $slaBreached = ($resAt > $expRes);
        }

        if ($isClosed) {
            // Clôturé avec respect / hors SLA
            if ($slaKnown) {
                if ($slaBreached) {
                    // Clôturé sans respect SLA : Vert+ (avertissement)
                    return '#20c997';
                }
                // Clôturé avec respect SLA : Vert
                return '#198754';
            }
            // Clôturé sans information SLA exploitable : vert par défaut
            return '#198754';
        }

        // Statuts non clôturés : palette métier
        switch ($statusKey) {
            case 'open':
                // Nouveau : Rouge
                return '#dc3545';
            case 'in_progress':
            case 'assigned':
                // En cours : Orange
                return '#fd7e14';
            case 'treated':
            case 'traite':
            case 'processed':
                // Traité : Bleu
                return '#0d6efd';
            case 'cancelled':
                // Annulé / hors workflow : Gris
                return '#6c757d';
        }

        // Fallback sur la couleur stockée en base, sinon gris neutre
        if ($baseColor !== '') {
            return $baseColor;
        }
        return '#6c757d';
    }
}
