import unittest
import tempfile
import os
import json

from slrquerytester.utils.file_manager import FileManager


class TestFileManager(unittest.TestCase):

    def setUp(self):
        self.temp_dir = tempfile.TemporaryDirectory()
        self.base_path = self.temp_dir.name

    def tearDown(self):
        self.temp_dir.cleanup()

    def test_read_config_nonexistent_file(self):
        """
        If the config file doesn't exist, should raise ValueError.
        """
        with self.assertRaises(ValueError):
            FileManager.read_config("nonexistent.json")

    def test_read_config_not_json_file(self):
        """
        If file extension is not .json, should raise ValueError.
        """
        fake_file = os.path.join(self.base_path, "config.txt")
        with open(fake_file, "w") as f:
            f.write("{}")  # Write valid JSON but wrong extension

        with self.assertRaises(ValueError):
            FileManager.read_config(fake_file)

    def test_read_config_corrupted_json(self):
        """
        If the .json file is corrupted, raise ValueError (JSONDecodeError).
        """
        bad_file = os.path.join(self.base_path, "config.json")
        with open(bad_file, "w") as f:
            f.write("{not valid json")

        with self.assertRaises(ValueError):
            FileManager.read_config(bad_file)

    def test_read_config_repository_mode(self):
        """
        If 'repository' is specified,
        we expect no other individual paths (like 'cache_directory')
        and default subdirs are set automatically.
        """
        config_file = os.path.join(self.base_path, "config.json")
        config_data = {
            "repository": os.path.join(self.base_path, "my_repo"),
            "api_keys": {"test_api": "secret"},
            "threshold_days": 42
        }
        with open(config_file, "w") as f:
            json.dump(config_data, f)

        api_keys, cache_dir, gold_dir, report_dir, threshold, queries_file, repo, manual_dir = \
            FileManager.read_config(config_file)

        # Check we got the right data back
        self.assertEqual(api_keys, {"test_api": "secret"})
        self.assertEqual(threshold, 42)
        self.assertEqual(repo, config_data["repository"])
        self.assertIn("slrquerytester.cache", cache_dir)
        self.assertIn("golden_solution", gold_dir)
        self.assertIn("reports", report_dir)
        self.assertIn("queries.json", queries_file)
        self.assertIn("manual_articles", manual_dir)

        # The repository path should have been created
        self.assertTrue(os.path.exists(repo), "Repository directory should be created.")

    def test_read_config_individual_paths(self):
        """
        If no 'repository' is specified,
        we either use user-provided paths or defaults.
        """
        config_file = os.path.join(self.base_path, "config.json")
        config_data = {
            "api_keys": {"key1": "val1"},
            "threshold_days": 10,
            "cache_directory": "my_cache",
            "golden_solution_directory": "my_golden",
            "report_directory": "my_reports",
            "queries_file": "my_queries.json",
            "manual_articles_directory": "my_manual"
        }
        with open(config_file, "w") as f:
            json.dump(config_data, f)

        api_keys, cache_dir, gold_dir, report_dir, threshold, queries_file, repo, manual_dir = \
            FileManager.read_config(config_file)
        self.assertEqual(api_keys, {"key1": "val1"})
        self.assertEqual(threshold, 10)
        self.assertEqual(cache_dir, "my_cache")
        self.assertEqual(gold_dir, "my_golden")
        self.assertEqual(report_dir, "my_reports")
        self.assertEqual(queries_file, "my_queries.json")
        self.assertEqual(manual_dir, "my_manual")
        self.assertIsNone(repo)

    def test_read_config_conflict(self):
        """
        Specifying both 'repository' and some individual path => ValueError.
        """
        config_file = os.path.join(self.base_path, "config.json")
        config_data = {
            "repository": "some_repo",
            "cache_directory": "some_cache"
        }
        with open(config_file, "w") as f:
            json.dump(config_data, f)

        with self.assertRaises(ValueError):
            FileManager.read_config(config_file)

    def test_read_queries_json_nonexistent(self):
        with self.assertRaises(ValueError):
            FileManager.read_queries_json("no_such_file.json")

    def test_read_queries_json_wrong_extension(self):
        # Create a .txt file
        fpath = os.path.join(self.base_path, "queries.txt")
        with open(fpath, "w") as f:
            f.write("[]")
        with self.assertRaises(ValueError):
            FileManager.read_queries_json(fpath)

    def test_read_queries_json_corrupted(self):
        fpath = os.path.join(self.base_path, "queries.json")
        with open(fpath, "w") as f:
            f.write("{ invalid: yes")

        with self.assertRaises(ValueError):
            FileManager.read_queries_json(fpath)

    def test_read_queries_json_valid(self):
        fpath = os.path.join(self.base_path, "queries.json")
        data = [
            {"query": "title:Hello", "decomposition_level": 1},
            {"query": "author:Smith", "decomposition_level": 2}
        ]
        with open(fpath, "w") as f:
            json.dump(data, f)

        result = FileManager.read_queries_json(fpath)
        self.assertEqual(len(result), 2)
        self.assertIn("query", result[0])
        self.assertEqual(result[0]["query"], "title:Hello")
        self.assertEqual(result[0]["decomposition_level"], 1)
        self.assertEqual(result[1]["query"], "author:Smith")
        self.assertEqual(result[1]["decomposition_level"], 2)

    def test_read_queries_json_missing_query_field(self):
        """
        If an item in the array lacks 'query', we raise ValueError.
        """
        fpath = os.path.join(self.base_path, "bad_queries.json")
        data = [
            {"NOTquery": "no query here", "decomposition_level": 1}
        ]
        with open(fpath, "w") as f:
            json.dump(data, f)

        with self.assertRaises(ValueError):
            FileManager.read_queries_json(fpath)

    def test_read_bibtex_directory_no_files(self):
        """
        If the directory has no .bib or .bibtex files, we raise RuntimeError.
        """
        empty_dir = os.path.join(self.base_path, "emptybib")
        os.makedirs(empty_dir, exist_ok=True)
        with self.assertRaises(RuntimeError):
            FileManager.read_bibtex_directory(empty_dir)

    def test_read_bibtex_directory_multiple_files(self):
        """
        If we have multiple .bib files, we parse them all
        and combine their articles.
        """
        bib_dir = os.path.join(self.base_path, "bibfiles")
        os.makedirs(bib_dir, exist_ok=True)

        content1 = r"""
        @article{refA,
          title={Paper A},
          author={Smith, John and Doe, Jane},
          year={2020}
        }
        """
        content2 = r"""
        @article{refB,
          title={Paper B},
          author={Brown, Alice},
          year={2021}
        }
        """
        file1 = os.path.join(bib_dir, "file1.bib")
        file2 = os.path.join(bib_dir, "file2.bib")
        with open(file1, "w") as f:
            f.write(content1)
        with open(file2, "w") as f:
            f.write(content2)

        articles = FileManager.read_bibtex_directory(bib_dir)
        self.assertEqual(len(articles.entries), 2)

if __name__ == "__main__":
    unittest.main()
