"""
Tests for new GitLab tracker methods (get_issue and get_comments).
"""

from unittest.mock import MagicMock, patch, AsyncMock
from unittest import IsolatedAsyncioTestCase

from preloop.sync.trackers.gitlab import GitLabTracker
from preloop.sync.exceptions import TrackerResponseError


class TestGitLabTrackerNewMethods(IsolatedAsyncioTestCase):
    """Test GitLab tracker's new get_issue and get_comments methods."""

    @patch("preloop.sync.trackers.gitlab.gitlab.Gitlab")
    async def test_get_comments_success(self, mock_gitlab_constructor):
        """Test successful comments retrieval."""
        # Arrange
        mock_gl_instance = MagicMock()
        mock_gitlab_constructor.return_value = mock_gl_instance

        mock_project = MagicMock()
        mock_project.path_with_namespace = "testgroup/testproject"
        mock_gl_instance.projects.get.return_value = mock_project

        mock_issue = MagicMock()
        mock_issue.id = 12345
        mock_issue.iid = 1
        mock_issue.web_url = "http://gitlab.com/testgroup/testproject/issues/1"
        mock_project.issues.get.return_value = mock_issue

        # Mock notes (comments)
        mock_note1 = MagicMock()
        mock_note1.id = 1001
        mock_note1.body = "First comment"
        mock_note1.system = False
        mock_note1.created_at = "2023-01-01T12:00:00.000Z"
        mock_note1.updated_at = "2023-01-01T12:00:00.000Z"
        mock_note1.author = {
            "id": 101,
            "username": "commenter1",
            "avatar_url": "http://gitlab.com/avatars/101.png",
        }

        mock_note2 = MagicMock()
        mock_note2.id = 1002
        mock_note2.body = "Second comment"
        mock_note2.system = False
        mock_note2.created_at = "2023-01-01T13:00:00.000Z"
        mock_note2.updated_at = "2023-01-01T13:00:00.000Z"
        mock_note2.author = {
            "id": 102,
            "username": "commenter2",
            "avatar_url": "http://gitlab.com/avatars/102.png",
        }

        mock_issue.notes.list.return_value = [mock_note1, mock_note2]

        connection_details = {"project_id": "proj-1"}
        tracker = GitLabTracker("tracker-1", "api-key", connection_details)
        tracker._make_request = AsyncMock()
        tracker._make_request.side_effect = [
            mock_project,
            mock_issue,
            [mock_note1, mock_note2],
        ]

        # Act
        result = await tracker.get_comments("1")

        # Assert
        self.assertEqual(len(result), 2)

        self.assertEqual(result[0].id, "1001")
        self.assertEqual(result[0].body, "First comment")
        self.assertEqual(result[0].author.id, "101")
        self.assertEqual(result[0].author.name, "commenter1")
        self.assertEqual(
            result[0].url, "http://gitlab.com/testgroup/testproject/issues/1#note_1001"
        )

        self.assertEqual(result[1].id, "1002")
        self.assertEqual(result[1].body, "Second comment")
        self.assertEqual(result[1].author.id, "102")
        self.assertEqual(result[1].author.name, "commenter2")

    @patch("preloop.sync.trackers.gitlab.gitlab.Gitlab")
    async def test_get_comments_missing_project_id(self, mock_gitlab_constructor):
        """Test error when project_id is missing from connection details."""
        # Arrange
        tracker = GitLabTracker("tracker-1", "api-key", {})

        # Act & Assert
        with self.assertRaises(TrackerResponseError) as context:
            await tracker.get_comments("1")

        self.assertIn(
            "Project ID not found in connection details", str(context.exception)
        )

    @patch("preloop.sync.trackers.gitlab.gitlab.Gitlab")
    async def test_get_comments_filters_system_notes(self, mock_gitlab_constructor):
        """Test that system notes are filtered out from comments."""
        # Arrange
        mock_gl_instance = MagicMock()
        mock_gitlab_constructor.return_value = mock_gl_instance

        mock_project = MagicMock()
        mock_project.path_with_namespace = "testgroup/testproject"
        mock_gl_instance.projects.get.return_value = mock_project

        mock_issue = MagicMock()
        mock_issue.id = 12345
        mock_issue.web_url = "http://gitlab.com/testgroup/testproject/issues/1"
        mock_project.issues.get.return_value = mock_issue

        # Mock notes with one system note that should be filtered
        mock_system_note = MagicMock()
        mock_system_note.id = 2000
        mock_system_note.system = True  # This should be filtered out

        mock_user_note = MagicMock()
        mock_user_note.id = 1001
        mock_user_note.body = "User comment"
        mock_user_note.system = False
        mock_user_note.created_at = "2023-01-01T12:00:00.000Z"
        mock_user_note.updated_at = "2023-01-01T12:00:00.000Z"
        mock_user_note.author = {
            "id": 101,
            "username": "commenter1",
            "avatar_url": "http://gitlab.com/avatars/101.png",
        }

        mock_issue.notes.list.return_value = [mock_system_note, mock_user_note]

        connection_details = {"project_id": "proj-1"}
        tracker = GitLabTracker("tracker-1", "api-key", connection_details)
        tracker._make_request = AsyncMock()
        tracker._make_request.side_effect = [
            mock_project,
            mock_issue,
            [mock_system_note, mock_user_note],
        ]

        # Act
        result = await tracker.get_comments("1")

        # Assert - only the user comment should be included
        self.assertEqual(len(result), 1)
        self.assertEqual(result[0].id, "1001")
        self.assertEqual(result[0].body, "User comment")

    @patch("preloop.sync.trackers.gitlab.gitlab.Gitlab")
    async def test_get_issue_missing_project_id(self, mock_gitlab_constructor):
        """Test error when project_id is missing from connection details."""
        # Arrange
        tracker = GitLabTracker("tracker-1", "api-key", {})

        # Act & Assert
        with self.assertRaises(TrackerResponseError) as context:
            await tracker.get_issue("1")

        self.assertIn(
            "Project ID not found in connection details", str(context.exception)
        )

    @patch("preloop.sync.trackers.gitlab.gitlab.Gitlab")
    async def test_get_issue_with_project_context(self, mock_gitlab_constructor):
        """Test get_issue uses project_id from connection_details."""
        # Arrange
        mock_gl_instance = MagicMock()
        mock_gitlab_constructor.return_value = mock_gl_instance

        mock_project = MagicMock()
        mock_project.path_with_namespace = "testgroup/testproject"
        mock_gl_instance.projects.get.return_value = mock_project

        mock_issue = MagicMock()
        mock_issue.id = 12345
        mock_issue.iid = 1
        mock_issue.title = "Test Issue"
        mock_issue.description = "Description for issue"
        mock_issue.state = "opened"
        mock_issue.created_at = "2023-01-01T10:00:00.000Z"
        mock_issue.updated_at = "2023-01-02T11:00:00.000Z"
        mock_issue.web_url = "http://gitlab.com/testgroup/testproject/issues/1"
        mock_issue.labels = ["bug", "critical"]
        mock_issue.assignees = [{"username": "user1"}]
        # Add attributes dict for the parser
        mock_issue.attributes = {
            "id": 12345,
            "iid": 1,
            "title": "Test Issue",
            "description": "Description for issue",
            "state": "opened",
            "created_at": "2023-01-01T10:00:00.000Z",
            "updated_at": "2023-01-02T11:00:00.000Z",
            "web_url": "http://gitlab.com/testgroup/testproject/issues/1",
            "labels": ["bug", "critical"],
            "assignee": None,
            "assignees": [],
            "author": {
                "id": 123,
                "name": "Author",
                "avatar_url": "https://example.com/avatar.png",
            },
            "_links": {"self": "http://gitlab.com/api/v4/projects/proj-1/issues/1"},
        }
        mock_project.issues.get.return_value = mock_issue

        connection_details = {"project_id": "proj-1"}
        tracker = GitLabTracker("tracker-1", "api-key", connection_details)
        tracker._make_request = AsyncMock()
        tracker._make_request.side_effect = [mock_project, mock_issue]

        # Act
        result = await tracker.get_issue("1")

        # Assert - Now result is an Issue object
        self.assertEqual(result.id, "12345")
        self.assertEqual(result.key, "testgroup/testproject#1")
        self.assertEqual(result.title, "Test Issue")
        self.assertEqual(result.description, "Description for issue")
        self.assertEqual(result.status.id, "opened")
        self.assertEqual(result.labels, ["bug", "critical"])
        self.assertEqual(result.url, "http://gitlab.com/testgroup/testproject/issues/1")

        # Verify the project_id from connection_details was used
        tracker._make_request.assert_any_call(mock_gl_instance.projects.get, "proj-1")
