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

"""
Unit tests for the Repository Service
"""

import unittest
from unittest.mock import patch, MagicMock
import os
import sys
import shutil
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.services.repository_service import (
    validate_github_url,
    normalize_github_url,
    extract_repo_name,
    clone_repository,
    get_repository_info,
    cleanup_repository,
    cleanup_all_repositories
)


class TestRepositoryService(unittest.TestCase):
    """Test cases for the repository service functions"""

    def setUp(self):
        """Set up test fixtures"""
        self.test_repo_dir = "test_repos"
        os.makedirs(self.test_repo_dir, exist_ok=True)
    
    def tearDown(self):
        """Tear down test fixtures"""
        if os.path.exists(self.test_repo_dir):
            shutil.rmtree(self.test_repo_dir)
    
    def test_validate_github_url(self):
        """Test validate_github_url function"""
        # Valid URLs
        self.assertTrue(validate_github_url("https://github.com/user/repo"))
        self.assertTrue(validate_github_url("https://github.com/user/repo.git"))
        self.assertTrue(validate_github_url("git@github.com:user/repo.git"))
        self.assertTrue(validate_github_url("https://github.com/user/repo-with-dash"))
        self.assertTrue(validate_github_url("https://github.com/user/repo_with_underscore"))
        
        # Invalid URLs
        self.assertFalse(validate_github_url("https://gitlab.com/user/repo"))
        self.assertFalse(validate_github_url("https://github.com"))
        self.assertFalse(validate_github_url("https://github.com/user"))
        self.assertFalse(validate_github_url("not a url"))
    
    def test_normalize_github_url(self):
        """Test normalize_github_url function"""
        # HTTPS URLs
        self.assertEqual(
            normalize_github_url("https://github.com/user/repo"),
            "https://github.com/user/repo.git"
        )
        self.assertEqual(
            normalize_github_url("https://github.com/user/repo.git"),
            "https://github.com/user/repo.git"
        )
        
        # SSH URLs
        self.assertEqual(
            normalize_github_url("git@github.com:user/repo.git"),
            "https://github.com/user/repo.git"
        )
        self.assertEqual(
            normalize_github_url("git@github.com:user/repo"),
            "https://github.com/user/repo.git"
        )
        
        # URLs with trailing slashes
        self.assertEqual(
            normalize_github_url("https://github.com/user/repo/"),
            "https://github.com/user/repo.git"
        )
        
        # Invalid URLs should return None
        self.assertIsNone(normalize_github_url("https://gitlab.com/user/repo"))
        self.assertIsNone(normalize_github_url("not a url"))
    
    def test_extract_repo_name(self):
        """Test extract_repo_name function"""
        self.assertEqual(extract_repo_name("https://github.com/user/repo"), "repo")
        self.assertEqual(extract_repo_name("https://github.com/user/repo.git"), "repo")
        self.assertEqual(extract_repo_name("git@github.com:user/repo.git"), "repo")
        self.assertEqual(extract_repo_name("https://github.com/user/repo-with-dash"), "repo-with-dash")
        
        # Invalid URLs should return None
        self.assertIsNone(extract_repo_name("https://github.com"))
        self.assertIsNone(extract_repo_name("not a url"))
    
    @patch('git.Repo.clone_from')
    def test_clone_repository(self, mock_clone_from):
        """Test clone_repository function"""
        # Mock the Git clone operation
        mock_repo = MagicMock()
        mock_clone_from.return_value = mock_repo
        
        # Test with default branch
        repo_path = clone_repository(
            "https://github.com/user/repo",
            output_dir=self.test_repo_dir
        )
        
        # Verify the result
        expected_path = os.path.join(self.test_repo_dir, "repo")
        self.assertEqual(repo_path, expected_path)
        mock_clone_from.assert_called_once()
        
        # Test with specific branch
        mock_clone_from.reset_mock()
        repo_path = clone_repository(
            "https://github.com/user/repo",
            branch="dev",
            output_dir=self.test_repo_dir
        )
        
        # Verify the result
        self.assertEqual(repo_path, expected_path)
        mock_clone_from.assert_called_once()
        
        # Test with invalid URL
        with self.assertRaises(ValueError):
            clone_repository(
                "not a url",
                output_dir=self.test_repo_dir
            )
    
    @patch('git.Repo')
    @patch('os.path.getsize')
    @patch('os.walk')
    def test_get_repository_info(self, mock_walk, mock_getsize, mock_repo):
        """Test get_repository_info function"""
        # Mock the Git repository
        mock_repo_instance = MagicMock()
        mock_repo.return_value = mock_repo_instance
        
        # Mock the active branch
        mock_branch = MagicMock()
        mock_branch.name = "main"
        mock_repo_instance.active_branch = mock_branch
        
        # Mock the head commit
        mock_commit = MagicMock()
        mock_commit.hexsha = "abc123"
        mock_repo_instance.head.commit = mock_commit
        
        # Mock the remote URL
        mock_remote = MagicMock()
        mock_remote.url = "https://github.com/user/repo.git"
        mock_repo_instance.remotes.origin = mock_remote
        
        # Mock the repository size
        mock_getsize.return_value = 1024
        
        # Mock the file count
        mock_walk.return_value = [
            ("/test/repo", ["dir1"], ["file1.py", "file2.py"]),
            ("/test/repo/dir1", [], ["file3.py"])
        ]
        
        # Test the function
        repo_info = get_repository_info("/test/repo")
        
        # Verify the result
        self.assertEqual(repo_info["branch"], "main")
        self.assertEqual(repo_info["commit"], "abc123")
        self.assertEqual(repo_info["remote_url"], "https://github.com/user/repo.git")
        self.assertEqual(repo_info["size"], 1024)
        self.assertEqual(repo_info["file_count"], 3)
    
    @patch('shutil.rmtree')
    @patch('os.path.exists')
    def test_cleanup_repository(self, mock_exists, mock_rmtree):
        """Test cleanup_repository function"""
        # Mock the path exists check
        mock_exists.return_value = True
        
        # Test the function
        cleanup_repository("/test/repo")
        
        # Verify the result
        mock_exists.assert_called_once_with("/test/repo")
        mock_rmtree.assert_called_once_with("/test/repo")
        
        # Test with non-existent path
        mock_exists.reset_mock()
        mock_rmtree.reset_mock()
        mock_exists.return_value = False
        
        cleanup_repository("/test/repo")
        
        mock_exists.assert_called_once_with("/test/repo")
        mock_rmtree.assert_not_called()
    
    @patch('os.listdir')
    @patch('os.path.isdir')
    @patch('shutil.rmtree')
    def test_cleanup_all_repositories(self, mock_rmtree, mock_isdir, mock_listdir):
        """Test cleanup_all_repositories function"""
        # Mock the directory listing
        mock_listdir.return_value = ["repo1", "repo2", "file.txt"]
        
        # Mock the isdir check
        mock_isdir.side_effect = lambda path: path.endswith("repo1") or path.endswith("repo2")
        
        # Test the function
        cleanup_all_repositories(self.test_repo_dir)
        
        # Verify the result
        mock_listdir.assert_called_once_with(self.test_repo_dir)
        self.assertEqual(mock_isdir.call_count, 3)  # Called for each item in the directory
        self.assertEqual(mock_rmtree.call_count, 2)  # Called for each directory


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