#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Unit tests for the Language Detector
"""

import unittest
from unittest.mock import patch, MagicMock, mock_open
import os
import sys
from pathlib import Path

# Add the project root directory to the Python path
project_root = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(project_root))

from src.core.language_detector import LanguageDetector


class TestLanguageDetector(unittest.TestCase):
    """Test cases for the LanguageDetector class"""

    def setUp(self):
        """Set up test fixtures"""
        self.detector = LanguageDetector()
        
        # Create a mock repository structure
        self.repo_path = "/test/repo"
        self.mock_files = [
            "/test/repo/main.py",
            "/test/repo/utils.py",
            "/test/repo/static/script.js",
            "/test/repo/static/style.css",
            "/test/repo/src/app.js",
            "/test/repo/src/components/Button.jsx",
            "/test/repo/src/components/Form.tsx",
            "/test/repo/docs/index.html",
            "/test/repo/README.md",
            "/test/repo/package.json",
            "/test/repo/Dockerfile",
            "/test/repo/.gitignore"
        ]
    
    def test_get_language_from_extension(self):
        """Test _get_language_from_extension method"""
        # Test common extensions
        self.assertEqual(self.detector._get_language_from_extension(".py"), "Python")
        self.assertEqual(self.detector._get_language_from_extension(".js"), "JavaScript")
        self.assertEqual(self.detector._get_language_from_extension(".jsx"), "JavaScript")
        self.assertEqual(self.detector._get_language_from_extension(".ts"), "TypeScript")
        self.assertEqual(self.detector._get_language_from_extension(".tsx"), "TypeScript")
        self.assertEqual(self.detector._get_language_from_extension(".java"), "Java")
        self.assertEqual(self.detector._get_language_from_extension(".go"), "Go")
        self.assertEqual(self.detector._get_language_from_extension(".rs"), "Rust")
        self.assertEqual(self.detector._get_language_from_extension(".html"), "HTML")
        self.assertEqual(self.detector._get_language_from_extension(".css"), "CSS")
        self.assertEqual(self.detector._get_language_from_extension(".md"), "Markdown")
        
        # Test unknown extension
        self.assertEqual(self.detector._get_language_from_extension(".unknown"), "Other")
    
    def test_get_language_from_filename(self):
        """Test _get_language_from_filename method"""
        # Test common filenames
        self.assertEqual(self.detector._get_language_from_filename("Dockerfile"), "Dockerfile")
        self.assertEqual(self.detector._get_language_from_filename(".gitignore"), "Git")
        self.assertEqual(self.detector._get_language_from_filename("package.json"), "JSON")
        self.assertEqual(self.detector._get_language_from_filename("README.md"), "Markdown")
        
        # Test unknown filename
        self.assertEqual(self.detector._get_language_from_filename("unknown"), None)
    
    @patch('os.walk')
    def test_detect_languages(self, mock_walk):
        """Test detect_languages method"""
        # Mock os.walk to return our mock files
        mock_walk.return_value = [
            ("/test/repo", ["static", "src", "docs"], ["main.py", "utils.py", "README.md", "package.json", ".gitignore"]),
            ("/test/repo/static", [], ["script.js", "style.css"]),
            ("/test/repo/src", ["components"], ["app.js"]),
            ("/test/repo/src/components", [], ["Button.jsx", "Form.tsx"]),
            ("/test/repo/docs", [], ["index.html"]),
        ]
        
        # Test the method
        languages = self.detector.detect_languages(self.repo_path)
        
        # Verify the result
        self.assertIn("Python", languages)
        self.assertIn("JavaScript", languages)
        self.assertIn("TypeScript", languages)
        self.assertIn("HTML", languages)
        self.assertIn("CSS", languages)
        self.assertIn("Markdown", languages)
        self.assertIn("JSON", languages)
        self.assertIn("Git", languages)
    
    @patch('os.walk')
    @patch('builtins.open', new_callable=mock_open, read_data="line1\nline2\nline3\n")
    def test_get_language_breakdown(self, mock_file, mock_walk):
        """Test get_language_breakdown method"""
        # Mock os.walk to return our mock files
        mock_walk.return_value = [
            ("/test/repo", ["static", "src"], ["main.py", "utils.py", "README.md"]),
            ("/test/repo/static", [], ["script.js"]),
            ("/test/repo/src", [], ["app.js"]),
        ]
        
        # Test the method
        breakdown = self.detector.get_language_breakdown(self.repo_path)
        
        # Verify the result
        self.assertIn("Python", breakdown)
        self.assertIn("JavaScript", breakdown)
        self.assertIn("Markdown", breakdown)
        
        # Each file has 4 lines (including the newline at the end)
        self.assertEqual(breakdown["Python"]["files"], 2)
        self.assertEqual(breakdown["Python"]["lines"], 8)  # 2 files * 4 lines
        self.assertEqual(breakdown["JavaScript"]["files"], 2)
        self.assertEqual(breakdown["JavaScript"]["lines"], 8)  # 2 files * 4 lines
        self.assertEqual(breakdown["Markdown"]["files"], 1)
        self.assertEqual(breakdown["Markdown"]["lines"], 4)  # 1 file * 4 lines
        
        # Check percentages
        total_lines = 20  # 5 files * 4 lines
        self.assertEqual(breakdown["Python"]["percentage"], 40)  # 8/20 * 100
        self.assertEqual(breakdown["JavaScript"]["percentage"], 40)  # 8/20 * 100
        self.assertEqual(breakdown["Markdown"]["percentage"], 20)  # 4/20 * 100
    
    @patch('os.path.isfile')
    def test_is_binary_file(self, mock_isfile):
        """Test _is_binary_file method"""
        # Mock isfile to always return True
        mock_isfile.return_value = True
        
        # Test with text file extensions
        self.assertFalse(self.detector._is_binary_file("test.py"))
        self.assertFalse(self.detector._is_binary_file("test.js"))
        self.assertFalse(self.detector._is_binary_file("test.html"))
        self.assertFalse(self.detector._is_binary_file("test.css"))
        self.assertFalse(self.detector._is_binary_file("test.md"))
        
        # Test with binary file extensions
        self.assertTrue(self.detector._is_binary_file("test.png"))
        self.assertTrue(self.detector._is_binary_file("test.jpg"))
        self.assertTrue(self.detector._is_binary_file("test.gif"))
        self.assertTrue(self.detector._is_binary_file("test.pdf"))
        self.assertTrue(self.detector._is_binary_file("test.zip"))
        
        # Test with non-existent file
        mock_isfile.return_value = False
        self.assertFalse(self.detector._is_binary_file("nonexistent.py"))
    
    @patch('os.path.isdir')
    def test_should_ignore_directory(self, mock_isdir):
        """Test _should_ignore_directory method"""
        # Mock isdir to always return True
        mock_isdir.return_value = True
        
        # Test with common directories to ignore
        self.assertTrue(self.detector._should_ignore_directory("/test/repo/node_modules"))
        self.assertTrue(self.detector._should_ignore_directory("/test/repo/.git"))
        self.assertTrue(self.detector._should_ignore_directory("/test/repo/__pycache__"))
        self.assertTrue(self.detector._should_ignore_directory("/test/repo/venv"))
        self.assertTrue(self.detector._should_ignore_directory("/test/repo/.vscode"))
        
        # Test with directories not to ignore
        self.assertFalse(self.detector._should_ignore_directory("/test/repo/src"))
        self.assertFalse(self.detector._should_ignore_directory("/test/repo/app"))
        self.assertFalse(self.detector._should_ignore_directory("/test/repo/docs"))
        
        # Test with non-existent directory
        mock_isdir.return_value = False
        self.assertFalse(self.detector._should_ignore_directory("/test/repo/nonexistent"))
    
    def test_should_ignore_file(self):
        """Test _should_ignore_file method"""
        # Test with common files to ignore
        self.assertTrue(self.detector._should_ignore_file("/test/repo/.DS_Store"))
        self.assertTrue(self.detector._should_ignore_file("/test/repo/Thumbs.db"))
        self.assertTrue(self.detector._should_ignore_file("/test/repo/.env"))
        
        # Test with files not to ignore
        self.assertFalse(self.detector._should_ignore_file("/test/repo/main.py"))
        self.assertFalse(self.detector._should_ignore_file("/test/repo/app.js"))
        self.assertFalse(self.detector._should_ignore_file("/test/repo/README.md"))


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