<?php
/**
 * FavMasToKey - Fonctions utilitaires (version améliorée)
 */

// Empêcher l'accès direct au fichier
if (!defined('FAVMASTOKEY')) {
    die('Accès direct interdit');
}

/**
 * Valide un fichier JSON de favoris Mastodon
 * 
 * @param string $file_path Chemin vers le fichier JSON
 * @return array|bool Tableau contenant les données du fichier ou false en cas d'erreur
 */
function validate_mastodon_json($file_path) {
    // Vérifier si le fichier existe
    if (!file_exists($file_path)) {
        return [
            'success' => false,
            'message' => 'Le fichier n\'existe pas.'
        ];
    }

    // Lire le contenu du fichier
    $content = file_get_contents($file_path);
    if (!$content) {
        return [
            'success' => false,
            'message' => 'Impossible de lire le contenu du fichier.'
        ];
    }

    // Décoder le JSON
    $json = json_decode($content, true);
    if (json_last_error() !== JSON_ERROR_NONE) {
        return [
            'success' => false,
            'message' => 'Le fichier n\'est pas un JSON valide: ' . json_last_error_msg()
        ];
    }

    // Vérifier la structure du fichier
    if (!isset($json['@context']) || !isset($json['type']) || !isset($json['orderedItems'])) {
        return [
            'success' => false,
            'message' => 'Le format du fichier JSON n\'est pas celui attendu pour un export de favoris Mastodon.'
        ];
    }

    // Vérifier que orderedItems est un tableau
    if (!is_array($json['orderedItems'])) {
        return [
            'success' => false,
            'message' => 'Le format des favoris dans le fichier est invalide.'
        ];
    }

    // Tout est OK
    return [
        'success' => true,
        'data' => $json,
        'count' => count($json['orderedItems'])
    ];
}

/**
 * Extrait les identifiants de publications à partir des URLs Mastodon
 * 
 * @param array $urls Tableau d'URLs Mastodon
 * @return array Tableau d'identifiants extraits
 */
function extract_toot_ids($urls) {
    $ids = [];
    
    foreach ($urls as $url) {
        // Format attendu: https://instance.tld/users/username/statuses/id
        $parts = explode('/', $url);
        
        // L'ID devrait être le dernier élément après "statuses"
        $id = end($parts);
        
        if (is_numeric($id)) {
            $ids[] = [
                'original_url' => $url,
                'toot_id' => $id,
                'instance' => parse_url($url, PHP_URL_HOST),
                'username' => isset($parts[count($parts) - 3]) ? $parts[count($parts) - 3] : null
            ];
        }
    }
    
    return $ids;
}

/**
 * Obtient l'URL de base d'une instance ActivityPub
 * 
 * @param string $url URL complète
 * @return string Domaine de l'instance
 */
function get_instance_domain($url) {
    $parsed = parse_url($url);
    return isset($parsed['host']) ? $parsed['host'] : '';
}

/**
 * Effectue une requête cURL vers l'API Misskey
 * 
 * @param string $instance Instance Misskey (ex: misskey.io)
 * @param string $endpoint Point d'accès API (ex: /api/notes/favorites/create)
 * @param array $data Données à envoyer
 * @param string $token Token d'accès OAuth
 * @param bool $with_timing Indique si les informations de timing doivent être retournées
 * @return array Résultat de la requête
 */
function misskey_api_request($instance, $endpoint, $data, $token, $with_timing = false) {
    global $config;
    
    $start_time = microtime(true);
    
    // Construire l'URL complète
    $url = "https://{$instance}{$endpoint}";
    
    // Ajouter le token d'accès aux données
    $data['i'] = $token;
    
    // Initialiser cURL
    $ch = curl_init();
    
    // Configurer la requête
    curl_setopt_array($ch, [
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($data),
        CURLOPT_HTTPHEADER => [
            'Content-Type: application/json',
            'User-Agent: FavMasToKey/' . $config['app_version']
        ],
        CURLOPT_TIMEOUT => $config['timeout'],
        CURLOPT_SSL_VERIFYPEER => true
    ]);
    
    // Exécuter la requête
    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $error = curl_error($ch);
    
    // Calcul du temps de réponse
    $end_time = microtime(true);
    $response_time = round(($end_time - $start_time) * 1000); // en millisecondes
    
    // Fermer la session cURL
    curl_close($ch);
    
    // Vérifier les erreurs
    if ($error) {
        $result = [
            'success' => false,
            'message' => 'Erreur cURL: ' . $error,
            'http_code' => $http_code,
            'error_code' => 'NETWORK_ERROR'
        ];
        
        if ($with_timing) {
            $result['timing'] = [
                'response_time_ms' => $response_time,
                'endpoint' => $endpoint
            ];
        }
        
        return $result;
    }
    
    // Décoder la réponse
    $response_data = json_decode($response, true);
    
    // Analyse spécifique des erreurs
    if ($http_code == 429) {
        $result = [
            'success' => false,
            'message' => 'Limite de taux (rate limit) dépassée. Réduisez la fréquence des requêtes ou activez le mode lent.',
            'http_code' => $http_code,
            'error_code' => 'RATE_LIMIT_EXCEEDED',
            'data' => $response_data
        ];
        
        if ($with_timing) {
            $result['timing'] = [
                'response_time_ms' => $response_time,
                'endpoint' => $endpoint
            ];
        }
        
        return $result;
    }
    
    // Vérifier si la requête a réussi
    if ($http_code >= 200 && $http_code < 300) {
        $result = [
            'success' => true,
            'data' => $response_data,
            'http_code' => $http_code
        ];
        
        if ($with_timing) {
            $result['timing'] = [
                'response_time_ms' => $response_time,
                'endpoint' => $endpoint
            ];
        }
        
        return $result;
    } else {
        // Déterminer le type d'erreur
        $error_code = 'API_ERROR';
        $error_message = 'Erreur API Misskey';
        
        if (isset($response_data['error'])) {
            if (isset($response_data['error']['code'])) {
                $error_code = $response_data['error']['code'];
            }
            if (isset($response_data['error']['message'])) {
                $error_message = $response_data['error']['message'];
            }
        }
        
        $result = [
            'success' => false,
            'message' => $error_message,
            'http_code' => $http_code,
            'error_code' => $error_code,
            'data' => $response_data
        ];
        
        if ($with_timing) {
            $result['timing'] = [
                'response_time_ms' => $response_time,
                'endpoint' => $endpoint
            ];
        }
        
        return $result;
    }
}

/**
 * Valide un jeton d'accès Misskey en effectuant une requête test
 * 
 * @param string $instance Instance Misskey
 * @param string $token Jeton d'accès à valider
 * @return array Résultat de la validation
 */
function validate_misskey_token($instance, $token) {
    // Test basique de connexion avec un simple ping
    $ping_result = misskey_api_request($instance, '/api/ping', [], $token);
    
    if (!$ping_result['success']) {
        return $ping_result;
    }
    
    // Test de récupération d'informations du compte actuel (permission la plus basique)
    $account_test = misskey_api_request($instance, '/api/i', [], $token);
    
    if (!$account_test['success']) {
        return [
            'success' => false,
            'message' => 'Le jeton est valide mais n\'a pas accès aux informations du compte. Assurez-vous d\'avoir accordé la permission "Afficher les informations du compte".',
            'data' => $account_test['data'],
            'error_code' => 'INSUFFICIENT_PERMISSIONS'
        ];
    }
    
    // Test de validation pour vérifier la permission d'ajouter aux favoris
    // On n'a pas besoin d'ajouter un favori réel, juste de vérifier si on peut voir les favoris
    $favorites_test = misskey_api_request($instance, '/api/i/favorites', ['limit' => 1], $token);
    
    if (!$favorites_test['success']) {
        return [
            'success' => false,
            'message' => 'Le jeton est valide mais n\'a pas accès aux favoris. Assurez-vous d\'avoir accordé les permissions "Afficher les favoris" et "Gérer les favoris".',
            'data' => $favorites_test['data'],
            'error_code' => 'INSUFFICIENT_PERMISSIONS'
        ];
    }
    
    // Tout est bon
    return [
        'success' => true,
        'message' => 'Jeton valide avec toutes les permissions nécessaires',
        'data' => [
            'account' => isset($account_test['data']['username']) ? $account_test['data']['username'] : 'Compte validé',
            'ping' => $ping_result['data']
        ]
    ];
}

/**
 * Vérifie si une note est déjà dans les favoris
 * 
 * @param string $instance Instance Misskey
 * @param string $note_id ID de la note à vérifier
 * @param string $token Token d'accès
 * @return array Résultat de la vérification
 */
function check_if_favorited($instance, $note_id, $token) {
    // Endpoint pour récupérer le statut de favori d'une note
    $endpoint = '/api/notes/show';
    
    // Données pour la requête
    $data = [
        'noteId' => $note_id
    ];
    
    // Effectuer la requête avec timing pour les statistiques de performance
    $result = misskey_api_request($instance, $endpoint, $data, $token, true);
    
    if ($result['success'] && isset($result['data'])) {
        // La note existe et nous avons une réponse
        // Vérifier si l'utilisateur a déjà mis cette note en favori
        if (isset($result['data']['isFavorited']) && $result['data']['isFavorited'] === true) {
            return [
                'success' => true,
                'is_favorited' => true,
                'message' => 'Cette note est déjà dans vos favoris',
                'timing' => isset($result['timing']) ? $result['timing'] : null
            ];
        } else {
            return [
                'success' => true,
                'is_favorited' => false,
                'message' => 'Cette note n\'est pas encore dans vos favoris',
                'timing' => isset($result['timing']) ? $result['timing'] : null
            ];
        }
    }
    
    // En cas d'erreur ou si la note n'existe pas
    return [
        'success' => false,
        'is_favorited' => false,
        'message' => isset($result['message']) ? $result['message'] : 'Impossible de vérifier le statut de favori',
        'error_code' => isset($result['error_code']) ? $result['error_code'] : 'UNKNOWN_ERROR',
        'timing' => isset($result['timing']) ? $result['timing'] : null
    ];
}

/**
 * Recherche une note Mastodon sur le réseau fédéré de Misskey
 * Utilise l'endpoint ap/show qui s'est avéré le plus fiable avec différentes instances
 * 
 * @param string $instance Instance Misskey
 * @param string $url URL de la publication Mastodon
 * @param string $token Token d'accès
 * @return array Résultat de la recherche
 */
function search_federated_note($instance, $url, $token) {
    // Nettoyer l'URL (enlever les éventuels paramètres)
    $cleanUrl = strtok($url, '?');
    
    // Journal de débogage
    error_log("Recherche fédérée pour: " . $cleanUrl);
    
    // Méthode principale: Utiliser ap/show qui fonctionne avec la plupart des instances
    $endpoint = '/api/ap/show';
    $data = [
        'uri' => $cleanUrl
    ];
    
    // Effectuer la requête avec timing pour les statistiques de performance
    $result = misskey_api_request($instance, $endpoint, $data, $token, true);
    
    // Journal pour le format de la réponse
    if ($result['success'] && isset($result['data'])) {
        error_log("Format de réponse ap/show: " . json_encode(array_keys($result['data'])));
    }
    
    // Si la méthode principale a réussi et renvoie un ID, retourner le résultat
    if ($result['success'] && isset($result['data'])) {
        // Vérifier si l'ID existe directement
        if (isset($result['data']['id'])) {
            return $result;
        }
        
        // Certaines instances peuvent avoir l'ID dans 'note'
        if (isset($result['data']['note']) && isset($result['data']['note']['id'])) {
            // Remonter l'ID au niveau principal
            $result['data']['id'] = $result['data']['note']['id'];
            return $result;
        }
        
        // Pour les instances plus récentes qui utilisent un format différent
        if (!empty($result['data'])) {
            // Rechercher un champ qui pourrait contenir l'ID
            foreach (['id', 'noteId', 'objectId', 'originalId'] as $possibleIdField) {
                if (isset($result['data'][$possibleIdField])) {
                    $result['data']['id'] = $result['data'][$possibleIdField];
                    return $result;
                }
            }
            
            // Si toujours pas d'ID, examiner la structure pour le trouver
            foreach ($result['data'] as $key => $value) {
                if (is_array($value) && isset($value['id'])) {
                    $result['data']['id'] = $value['id'];
                    return $result;
                }
            }
        }
    }
    
    // Vérifier si c'est une erreur de rate limiting
    if (!$result['success'] && isset($result['error_code']) && $result['error_code'] === 'RATE_LIMIT_EXCEEDED') {
        return $result;
    }
    
    // Méthode de secours 1: Essayer notes/search-by-url (parfois utilisé dans les anciennes versions)
    $fallback_result = misskey_api_request($instance, '/api/notes/search-by-url', ['url' => $cleanUrl], $token, true);
    
    if ($fallback_result['success'] && isset($fallback_result['data'])) {
        error_log("Format de réponse search-by-url: " . json_encode(array_keys($fallback_result['data'])));
        
        // Vérifier si nous avons un résultat avec un ID
        if (isset($fallback_result['data']['id'])) {
            return $fallback_result;
        }
        
        // Si le résultat est un tableau (certaines instances renvoient un tableau)
        if (is_array($fallback_result['data']) && !isset($fallback_result['data']['id'])) {
            // Chercher le premier élément avec un ID
            foreach ($fallback_result['data'] as $item) {
                if (is_array($item) && isset($item['id'])) {
                    $fallback_result['data'] = $item; // Utiliser cet élément comme résultat
                    return $fallback_result;
                }
            }
        }
    }
    
    // Vérifier à nouveau si c'est une erreur de rate limiting
    if (!$fallback_result['success'] && isset($fallback_result['error_code']) && $fallback_result['error_code'] === 'RATE_LIMIT_EXCEEDED') {
        return $fallback_result;
    }
    
    // Méthode de secours 2: Extraction et recherche par ID distant
    $urlParts = parse_url($cleanUrl);
    if (isset($urlParts['path'])) {
        $pathParts = explode('/', trim($urlParts['path'], '/'));
        
        if (count($pathParts) >= 4 && $pathParts[count($pathParts) - 2] === 'statuses') {
            $statusId = end($pathParts);
            $username = $pathParts[count($pathParts) - 3];
            $acctDomain = isset($urlParts['host']) ? $urlParts['host'] : '';
            
            if ($statusId && $username && $acctDomain) {
                $remoteId = "https://{$acctDomain}/users/{$username}/statuses/{$statusId}";
                
                // Essayer d'abord avec /api/notes/show
                $remote_result = misskey_api_request($instance, '/api/notes/show', ['uri' => $remoteId], $token, true);
                
                if ($remote_result['success'] && isset($remote_result['data']['id'])) {
                    return $remote_result;
                }
                
                // Vérifier si c'est une erreur de rate limiting
                if (!$remote_result['success'] && isset($remote_result['error_code']) && $remote_result['error_code'] === 'RATE_LIMIT_EXCEEDED') {
                    return $remote_result;
                }
                
                // Dernier recours: essayer renotes/search
                $renote_result = misskey_api_request($instance, '/api/notes/search', [
                    'query' => "@{$username}@{$acctDomain} {$statusId}",
                    'limit' => 10
                ], $token, true);
                
                if ($renote_result['success'] && !empty($renote_result['data'])) {
                    // Parcourir les résultats pour trouver une correspondance
                    foreach ($renote_result['data'] as $note) {
                        if (isset($note['id'])) {
                            $renote_result['data'] = $note;
                            return $renote_result;
                        }
                    }
                }
                
                // Vérifier si c'est une erreur de rate limiting
                if (!$renote_result['success'] && isset($renote_result['error_code']) && $renote_result['error_code'] === 'RATE_LIMIT_EXCEEDED') {
                    return $renote_result;
                }
            }
        }
    }
    
    // Si aucune méthode n'a fonctionné, retourner une erreur
    return [
        'success' => false,
        'message' => "Impossible de trouver la publication sur le réseau fédéré après plusieurs tentatives",
        'error_code' => 'NOT_FOUND',
        'http_code' => isset($result['http_code']) ? $result['http_code'] : 404,
        'timing' => isset($result['timing']) ? $result['timing'] : null
    ];
}

/**
 * Ajoute une note aux favoris sur Misskey
 * 
 * @param string $instance Instance Misskey
 * @param string $note_id ID de la note à ajouter aux favoris
 * @param string $token Token d'accès
 * @return array Résultat de l'opération
 */
function add_to_favorites($instance, $note_id, $token) {
    global $config;
    
    // Endpoint pour ajouter aux favoris
    $endpoint = $config['misskey_api_endpoint'];
    
    // Données pour l'ajout aux favoris
    $data = [
        'noteId' => $note_id
    ];
    
    // Effectuer la requête avec timing pour les statistiques de performance
    $result = misskey_api_request($instance, $endpoint, $data, $token, true);
    
    return $result;
}

/**
 * Récupère les favoris existants sur Misskey (pour une vérification en masse)
 * 
 * @param string $instance Instance Misskey
 * @param string $token Token d'accès
 * @param int $limit Nombre de favoris à récupérer par page
 * @param string $untilId ID pour la pagination
 * @return array Liste des favoris et statut de la requête
 */
function get_existing_favorites($instance, $token, $limit = 100, $untilId = null) {
    // Endpoint pour récupérer les favoris
    $endpoint = '/api/i/favorites';
    
    // Données pour la requête
    $data = [
        'limit' => $limit
    ];
    
    // Ajouter l'ID pour la pagination si fourni
    if ($untilId) {
        $data['untilId'] = $untilId;
    }
    
    // Effectuer la requête
    $result = misskey_api_request($instance, $endpoint, $data, $token);
    
    if ($result['success'] && isset($result['data']) && is_array($result['data'])) {
        // Extraire les IDs
        $favoriteIds = [];
        foreach ($result['data'] as $favorite) {
            if (isset($favorite['id'])) {
                $favoriteIds[] = $favorite['id'];
            }
        }
        
        // Déterminer s'il y a plus de résultats (pour la pagination)
        $hasMore = count($result['data']) >= $limit;
        $lastId = $hasMore && !empty($result['data']) ? end($result['data'])['id'] : null;
        
        return [
            'success' => true,
            'favorites' => $favoriteIds,
            'has_more' => $hasMore,
            'last_id' => $lastId,
            'count' => count($favoriteIds)
        ];
    }
    
    return [
        'success' => false,
        'message' => isset($result['message']) ? $result['message'] : 'Impossible de récupérer les favoris',
        'error_code' => isset($result['error_code']) ? $result['error_code'] : 'UNKNOWN_ERROR'
    ];
}

/**
 * Récupère les performances de l'API pour un domaine spécifique
 * et recommande un délai adapté
 * 
 * @param string $domain Domaine de l'instance
 * @param array $stats Statistiques de performance
 * @return int Délai recommandé en secondes
 */
function get_recommended_delay_for_domain($domain, $stats) {
    // Valeurs par défaut
    $baseDelay = 3; // secondes
    $increment = 5; // secondes par échec de rate-limit
    
    if (!$stats || !isset($stats['rateLimitCount'])) {
        return $baseDelay;
    }
    
    // Calculer un délai basé sur le nombre d'erreurs de rate-limit
    $rateLimitCount = (int)$stats['rateLimitCount'];
    
    if ($rateLimitCount > 0) {
        // Formule: délai de base + (nombre d'erreurs de rate-limit * incrément)
        // Limité à un maximum de 300 secondes (5 minutes)
        $recommendedDelay = min(300, $baseDelay + ($rateLimitCount * $increment));
        return $recommendedDelay;
    }
    
    return $baseDelay;
}