import json
import sys
import tempfile
import unittest
from pathlib import Path

from core.io import json_store

PROJECT_ROOT = Path(__file__).resolve().parent.parent

sys.path.insert(0, str(PROJECT_ROOT))

from core import constants
from core.services import deploy_service as manager


class TestCSSModDeployer(unittest.TestCase):
    def setUp(self) -> None:
        self.sandbox = tempfile.TemporaryDirectory()
        self.sandbox_path = Path(self.sandbox.name)

        constants.SERVER_ROOT = self.sandbox_path
        constants.SERVER_CSTRIKE = self.sandbox_path / "cstrike"
        constants.REPOSITORY = self.sandbox_path / "css_mod_manager" / "repository"
        constants.CONFIG_FILE = self.sandbox_path / "css_mod_manager" / "config.json"
        constants.DEPLOY_MANIFEST = self.sandbox_path / "css_mod_manager" / "deployed_files.json"

        constants.SERVER_CSTRIKE.mkdir(parents=True, exist_ok=True)
        constants.REPOSITORY.mkdir(parents=True, exist_ok=True)

        json_store.save_config({"load_order": []})
        json_store.save_deployed_manifest({})

    def tearDown(self) -> None:
        self.sandbox.cleanup()

    def test_activation_and_deployment(self) -> None:
        """Verify that the files for an active mod are copied to the deployment."""
        mod_dir = constants.REPOSITORY / "my_mod"
        mod_dir.mkdir(parents=True, exist_ok=True)

        with open(mod_dir / "manifest.json", "w", encoding="utf-8") as f:
            json.dump({"id": "my_mod", "name": "My Mod", "config_files": {}}, f)

        plugin = mod_dir / "cstrike" / "addons" / "sourcemod" / "plugins" / "test.smx"
        plugin.parent.mkdir(parents=True, exist_ok=True)
        plugin.write_text("binary", encoding="utf-8")

        with open(constants.CONFIG_FILE, "w", encoding="utf-8") as f:
            json.dump({"load_order": ["my_mod"]}, f)

        stats = manager.deploy_mods(force_full_deploy=False)

        server_plugin = constants.SERVER_CSTRIKE / "addons" / "sourcemod" / "plugins" / "test.smx"
        self.assertTrue(server_plugin.exists())
        self.assertEqual(stats["files_copied"], 1)

    def test_desactivation_and_clean_server(self) -> None:
        """Verifies that when a mod is disabled, its files are removed from the server without affecting the rest."""
        server_file = constants.SERVER_CSTRIKE / "addons" / "sourcemod" / "plugins" / "old.smx"
        server_file.parent.mkdir(parents=True, exist_ok=True)
        server_file.write_text("content", encoding="utf-8")

        with open(constants.DEPLOY_MANIFEST, "w", encoding="utf-8") as f:
            json.dump({"mod_to_delete": ["cstrike/addons/sourcemod/plugins/old.smx"]}, f)

        with open(constants.CONFIG_FILE, "w", encoding="utf-8") as f:
            json.dump({"load_order": []}, f)

        manager.deploy_mods(force_full_deploy=False)

        self.assertFalse(server_file.exists())
        self.assertFalse((constants.SERVER_CSTRIKE / "addons").exists())
        self.assertTrue(constants.SERVER_CSTRIKE.exists())

    def test_clean_server_removes_all_files_for_disabled_mods(self):
        """Verifies that disabling multiple mods properly removes all their files from the server."""
        mod1_path = self.sandbox_path / "css_mod_manager" / "repository" / "mod_un"
        mod2_path = self.sandbox_path / "css_mod_manager" / "repository" / "mod_deux"

        (mod1_path / "cstrike" / "addons" / "sourcemod" / "plugins").mkdir(
            parents=True, exist_ok=True
        )
        (mod2_path / "cstrike" / "cfg").mkdir(parents=True, exist_ok=True)

        file1 = mod1_path / "cstrike" / "addons" / "sourcemod" / "plugins" / "plugin1.smx"
        file2 = mod2_path / "cstrike" / "cfg" / "config_custom.cfg"
        file1.write_text("// Faux plugin")
        file2.write_text("// Fausse config")

        with (mod1_path / "manifest.json").open("w", encoding="utf-8") as f:
            json.dump({"id": "mod_un", "name": "Mod Un", "config_files": {}}, f)
        with (mod2_path / "manifest.json").open("w", encoding="utf-8") as f:
            json.dump({"id": "mod_deux", "name": "Mod Deux", "config_files": {}}, f)

        json_store.save_config({"load_order": ["mod_un", "mod_deux"]})

        manager.deploy_mods(force_full_deploy=True)

        server_file1 = (
            self.sandbox_path / "cstrike" / "addons" / "sourcemod" / "plugins" / "plugin1.smx"
        )
        server_file2 = self.sandbox_path / "cstrike" / "cfg" / "config_custom.cfg"
        self.assertTrue(server_file1.exists())
        self.assertTrue(server_file2.exists())

        deployed_manifest = json_store.load_deployed_manifest()
        self.assertIn("mod_un", deployed_manifest)
        self.assertIn("mod_deux", deployed_manifest)

        manager.clean_server(active_mods=[])

        self.assertFalse(server_file1.exists(), "Le fichier du mod_un n'a pas été supprimé.")
        self.assertFalse(server_file2.exists(), "Le fichier du mod_deux n'a pas été supprimé.")

        self.assertFalse(
            (self.sandbox_path / "cstrike" / "addons" / "sourcemod").exists(),
            "Les dossiers parents vides n'ont pas été nettoyés.",
        )
        self.assertFalse(
            (self.sandbox_path / "cstrike" / "cfg").exists(),
            "Le dossier cfg vide n'a pas été nettoyé.",
        )

        new_deployed_manifest = json_store.load_deployed_manifest()
        self.assertNotIn("mod_un", new_deployed_manifest)
        self.assertNotIn("mod_deux", new_deployed_manifest)

    def test_deploy_solo_disabled_mod_purges_files(self) -> None:
        """Verify that deploying a disabled mod on your own removes its files from the server and cleans up the manifest."""
        mod_id = "my_disabled_mod"

        fake_server_file = constants.SERVER_ROOT / "cstrike/addons/plugin.ext"
        fake_server_file.parent.mkdir(parents=True, exist_ok=True)
        fake_server_file.write_text("dummy content")

        json_store.save_deployed_manifest({mod_id: ["cstrike/addons/plugin.ext"]})

        result = manager.deploy_mods(specific_mod_id=mod_id)

        self.assertFalse(
            fake_server_file.exists(), "Le fichier du mod désactivé aurait dû être purgé."
        )
        self.assertEqual(result["action"], "cleaned")
        self.assertEqual(result["files_deleted"], 1)

        updated_manifest = json_store.load_deployed_manifest()
        self.assertNotIn(mod_id, updated_manifest)

    @unittest.skip(
        "Crash temporaire dû aux effets de bord de generate_overrides - à corriger plus tard"
    )
    def test_global_deploy_syncs_deployed_manifest_correctly(self) -> None:
        """Verify that the global deployment does not overwrite the manifest with stale data."""
        active_mod = "mod_actif"
        disabled_mod = "mod_desactive"

        json_store.save_config({"load_order": [active_mod]})

        mod_path = constants.REPOSITORY / active_mod
        (mod_path / "cstrike").mkdir(parents=True, exist_ok=True)
        with (mod_path / "manifest.json").open("w", encoding="utf-8") as f:
            json.dump({"name": "Mod Actif", "version": "1.0"}, f)
        (mod_path / "cstrike/cfg/config.cfg").write_text("// cvar")

        json_store.save_deployed_manifest({active_mod: [], disabled_mod: ["cstrike/cfg/old.cfg"]})

        fake_old_file = constants.SERVER_ROOT / "cstrike/cfg/old.cfg"
        fake_old_file.parent.mkdir(parents=True, exist_ok=True)
        fake_old_file.write_text("// old")

        manager.deploy_mods(specific_mod_id=None)

        self.assertFalse(fake_old_file.exists())

        final_manifest = json_store.load_deployed_manifest()
        self.assertNotIn(
            disabled_mod,
            final_manifest,
            "Le mod désactivé a été réincarné par des données obsolètes !",
        )
        self.assertIn(active_mod, final_manifest)

    def test_deploy_active_mod_delta_cleanup(self) -> None:
        """Ensures that if a file is deleted locally from an active mod, the deployment removes it from the server."""
        mod_id = "mymod"
        json_store.save_config({"load_order": [mod_id]})

        mod_path = constants.REPOSITORY / mod_id
        (mod_path / "cstrike").mkdir(parents=True, exist_ok=True)
        with (mod_path / "manifest.json").open("w", encoding="utf-8") as f:
            json.dump({"name": "My Mod", "version": "1.0"}, f)

        (mod_path / "cstrike/fileA.txt").write_text("A")

        json_store.save_deployed_manifest({mod_id: ["cstrike/fileA.txt", "cstrike/fileB.txt"]})

        server_file_b = constants.SERVER_ROOT / "cstrike/fileB.txt"
        server_file_b.parent.mkdir(parents=True, exist_ok=True)
        server_file_b.write_text("B")

        manager.deploy_mods(specific_mod_id=mod_id)

        self.assertFalse(
            server_file_b.exists(), "Le fichier orphelin B doit être retiré du serveur."
        )

        final_manifest = json_store.load_deployed_manifest()
        self.assertEqual(final_manifest[mod_id], ["cstrike/fileA.txt"])


if __name__ == "__main__":
    unittest.main()
