"""Tests unitaires pour le module utils.gitea.

Ces tests vérifient les fonctions d'interaction avec l'API Gitea.
"""

import base64
import os
from datetime import datetime, timezone
from unittest.mock import MagicMock, Mock, mock_open, patch

import pytest
import requests

from utils.gitea import (
    charger_arborescence_fiches,
    charger_instructions_depuis_gitea,
    charger_schema_depuis_gitea,
    lire_fichier_local,
    recuperer_date_dernier_commit,
)


class TestLireFichierLocal:
    """Tests pour la fonction lire_fichier_local."""

    def test_lecture_fichier_utf8(self, tmp_path):
        """Test la lecture d'un fichier UTF-8 standard."""
        fichier = tmp_path / "test.txt"
        contenu_attendu = "Contenu de test avec caractères spéciaux: éàç"
        fichier.write_text(contenu_attendu, encoding="utf-8")

        resultat = lire_fichier_local(str(fichier))

        assert resultat == contenu_attendu

    def test_lecture_fichier_avec_accents(self, tmp_path):
        """Test la lecture d'un fichier avec caractères accentués."""
        fichier = tmp_path / "accents.txt"
        contenu = "Voici des accents: é, è, à, ç, ù"
        fichier.write_text(contenu, encoding="utf-8")

        resultat = lire_fichier_local(str(fichier))

        assert resultat == contenu

    def test_lecture_fichier_vide(self, tmp_path):
        """Test la lecture d'un fichier vide."""
        fichier = tmp_path / "vide.txt"
        fichier.write_text("", encoding="utf-8")

        resultat = lire_fichier_local(str(fichier))

        assert resultat == ""

    def test_fichier_inexistant(self):
        """Test la gestion d'un fichier inexistant."""
        with pytest.raises(FileNotFoundError):
            lire_fichier_local("fichier_inexistant.txt")


class TestRecupererDateDernierCommit:
    """Tests pour la fonction recuperer_date_dernier_commit."""

    @patch("utils.gitea.requests.get")
    def test_recuperation_date_commit(self, mock_get):
        """Test la récupération de la date du dernier commit."""
        # Mock de la réponse Gitea
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = [
            {
                "commit": {
                    "author": {
                        "date": "2025-01-15T10:30:00Z"
                    }
                }
            }
        ]
        mock_get.return_value = mock_response

        resultat = recuperer_date_dernier_commit("https://gitea.example.com/api/v1/repos/org/repo/commits")

        assert resultat is not None
        assert isinstance(resultat, datetime)
        assert resultat.year == 2025
        assert resultat.month == 1
        assert resultat.day == 15

    @patch("utils.gitea.requests.get")
    def test_aucun_commit(self, mock_get):
        """Test avec un dépôt sans commits."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = []
        mock_get.return_value = mock_response

        resultat = recuperer_date_dernier_commit("https://gitea.example.com/api/v1/repos/org/repo/commits")

        assert resultat is None

    @patch("utils.gitea.requests.get")
    def test_erreur_requete(self, mock_get):
        """Test la gestion d'une erreur de requête."""
        mock_get.side_effect = requests.RequestException("Network error")

        resultat = recuperer_date_dernier_commit("https://gitea.example.com/api/v1/repos/org/repo/commits")

        assert resultat is None


class TestChargerInstructionsDepuisGitea:
    """Tests pour la fonction charger_instructions_depuis_gitea."""

    @patch("utils.gitea.requests.get")
    @patch("utils.gitea.recuperer_date_dernier_commit")
    @patch("os.path.exists")
    @patch("os.path.getmtime")
    def test_telechargement_fichier_inexistant(self, mock_getmtime, mock_exists, mock_date_commit, mock_get):
        """Test le téléchargement quand le fichier local n'existe pas."""
        # Fichier local n'existe pas
        mock_exists.return_value = False

        # Commit distant disponible
        mock_date_commit.return_value = datetime(2025, 1, 15, tzinfo=timezone.utc)

        # Contenu encodé en base64
        contenu_md = "# Instructions\nCeci est un test"
        contenu_base64 = base64.b64encode(contenu_md.encode("utf-8")).decode("utf-8")

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"content": contenu_base64}
        mock_get.return_value = mock_response

        with patch("builtins.open", mock_open()) as mock_file:
            resultat = charger_instructions_depuis_gitea("Instructions.md")

            # Vérifie que le fichier a été écrit
            mock_file.assert_called_once_with("Instructions.md", "w", encoding="utf-8")
            assert resultat == contenu_md

    @patch("utils.gitea.requests.get")
    @patch("utils.gitea.recuperer_date_dernier_commit")
    @patch("os.path.exists")
    @patch("os.path.getmtime")
    @patch("builtins.open", new_callable=mock_open, read_data="# Local Instructions")
    def test_utilisation_cache_local_recent(self, mock_file, mock_getmtime, mock_exists, mock_date_commit, mock_get):
        """Test l'utilisation du cache local si plus récent."""
        # Fichier local existe
        mock_exists.return_value = True

        # Date fichier local plus récent
        local_time = datetime(2025, 1, 20, tzinfo=timezone.utc)
        mock_getmtime.return_value = local_time.timestamp()

        # Date commit distant plus ancien
        mock_date_commit.return_value = datetime(2025, 1, 15, tzinfo=timezone.utc)

        resultat = charger_instructions_depuis_gitea("Instructions.md")

        # Doit lire le fichier local sans appeler l'API
        assert mock_get.call_count == 0
        assert resultat == "# Local Instructions"

    @patch("utils.gitea.requests.get")
    @patch("utils.gitea.recuperer_date_dernier_commit")
    def test_erreur_reseau_avec_cache(self, mock_date_commit, mock_get):
        """Test le fallback sur le cache en cas d'erreur réseau."""
        mock_date_commit.side_effect = requests.RequestException("Network error")

        with patch("os.path.exists", return_value=True):
            with patch("builtins.open", mock_open(read_data="# Cached content")):
                resultat = charger_instructions_depuis_gitea("Instructions.md")

                assert resultat == "# Cached content"

    @patch("utils.gitea.requests.get")
    @patch("utils.gitea.recuperer_date_dernier_commit")
    @patch("os.path.exists")
    def test_erreur_reseau_sans_cache(self, mock_exists, mock_date_commit, mock_get):
        """Test le retour None si erreur et pas de cache."""
        mock_exists.return_value = False
        mock_date_commit.side_effect = requests.RequestException("Network error")

        resultat = charger_instructions_depuis_gitea("Instructions.md")

        assert resultat is None


class TestChargerSchemaDepuisGitea:
    """Tests pour la fonction charger_schema_depuis_gitea."""

    @patch("utils.gitea.requests.get")
    @patch("utils.gitea.recuperer_date_dernier_commit")
    @patch("os.path.exists")
    @patch("os.path.getmtime")
    def test_telechargement_schema_file(self, mock_getmtime, mock_exists, mock_date_commit, mock_get):
        """Test le téléchargement d'un fichier schema depuis Gitea."""
        # Fichier local n'existe pas
        mock_exists.return_value = False

        # Commit distant disponible
        mock_date_commit.return_value = datetime(2025, 1, 15, tzinfo=timezone.utc)

        # Contenu DOT encodé en base64
        contenu_dot = "digraph G { A -> B; }"
        contenu_base64 = base64.b64encode(contenu_dot.encode("utf-8")).decode("utf-8")

        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"content": contenu_base64}
        mock_get.return_value = mock_response

        with patch("builtins.open", mock_open()) as mock_file:
            resultat = charger_schema_depuis_gitea("test_schema.txt")

            # Vérifie que le fichier a été écrit
            assert mock_file.called
            assert resultat == "OK"

    @patch("utils.gitea.requests.get")
    @patch("utils.gitea.recuperer_date_dernier_commit")
    @patch("os.path.exists")
    @patch("os.path.getmtime")
    @patch("builtins.open", new_callable=mock_open)
    def test_cache_schema_file(self, mock_file, mock_getmtime, mock_exists, mock_date_commit, mock_get):
        """Test l'utilisation du cache pour le fichier schema."""
        # Fichier local existe et plus récent
        mock_exists.return_value = True
        local_time = datetime(2025, 1, 20, tzinfo=timezone.utc)
        mock_getmtime.return_value = local_time.timestamp()

        # Date commit distant plus ancien
        mock_date_commit.return_value = datetime(2025, 1, 15, tzinfo=timezone.utc)

        # Mock response pour le premier appel (get file info)
        contenu_base64 = base64.b64encode("digraph G { cached }".encode("utf-8")).decode("utf-8")
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = {"content": contenu_base64}
        mock_get.return_value = mock_response

        resultat = charger_schema_depuis_gitea("test_schema.txt")

        # Doit retourner OK sans réécrire (fichier déjà à jour)
        assert resultat == "OK"

    @patch("utils.gitea.requests.get")
    def test_erreur_chargement_schema(self, mock_get):
        """Test la gestion d'erreur lors du chargement du schema."""
        mock_get.side_effect = requests.RequestException("Network error")

        resultat = charger_schema_depuis_gitea("test_schema.txt")

        assert resultat is None


class TestChargerArborescenceFiches:
    """Tests pour la fonction charger_arborescence_fiches."""

    @patch("utils.gitea.requests.get")
    def test_arborescence_vide(self, mock_get):
        """Test avec un dépôt sans dossiers."""
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.json.return_value = []
        mock_get.return_value = mock_response

        resultat = charger_arborescence_fiches()

        assert resultat == {}

    @patch("utils.gitea.requests.get")
    def test_arborescence_avec_dossiers(self, mock_get):
        """Test avec des dossiers contenant des fiches."""
        # Mock réponse pour la liste des dossiers
        mock_response_dossiers = Mock()
        mock_response_dossiers.status_code = 200
        mock_response_dossiers.json.return_value = [
            {"name": "Composants", "type": "dir", "url": "https://gitea.example.com/api/v1/repos/org/repo/contents/Documents/Composants"}
        ]

        # Mock réponse pour le contenu du dossier
        mock_response_fichiers = Mock()
        mock_response_fichiers.status_code = 200
        mock_response_fichiers.json.return_value = [
            {"name": "Processeur.md", "download_url": "https://gitea.example.com/download/Processeur.md"},
            {"name": "Memoire.md", "download_url": "https://gitea.example.com/download/Memoire.md"}
        ]

        # Configure les appels successifs
        mock_get.side_effect = [mock_response_dossiers, mock_response_fichiers]

        resultat = charger_arborescence_fiches()

        assert "Composants" in resultat
        assert len(resultat["Composants"]) == 2
        assert resultat["Composants"][0]["nom"] == "Memoire.md"  # Trié alphabétiquement
        assert resultat["Composants"][1]["nom"] == "Processeur.md"

    @patch("utils.gitea.requests.get")
    def test_erreur_requete(self, mock_get):
        """Test la gestion d'erreur lors de la requête."""
        mock_get.side_effect = requests.RequestException("Network error")

        resultat = charger_arborescence_fiches()

        assert resultat == {}
