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

import pytest
from sqlalchemy.orm import Session

from preloop.models.models import (
    Organization,
    Project,
    Tracker,
    Issue,
    Comment,
)
from preloop.sync.scanner.core import TrackerClient


@pytest.fixture
def mock_db_session():
    """Fixture for a mock database session."""
    return MagicMock(spec=Session)


@pytest.fixture
def mock_tracker():
    """Fixture for a mock tracker model instance."""
    tracker = MagicMock(spec=Tracker)
    tracker.id = 1
    tracker.tracker_type = "github"
    tracker.account_id = "acc_123"
    return tracker


@pytest.fixture
def mock_organization():
    """Fixture for a mock organization model instance."""
    org = MagicMock(spec=Organization)
    org.id = 101
    org.identifier = "test-org"
    return org


@pytest.fixture
def mock_project():
    """Fixture for a mock project model instance."""
    project = MagicMock(spec=Project)
    project.id = 1001
    project.identifier = "test-project"
    return project


@pytest.mark.asyncio
@patch("preloop.sync.scanner.core.crud_issue")
@patch("preloop.sync.scanner.core.crud_comment")
@patch("preloop.sync.scanner.core.crud_issue_embedding")
async def test_scan_issues_handles_string_casting_and_identifier(
    mock_crud_embedding,
    mock_crud_comment,
    mock_crud_issue,
    mock_db_session,
    mock_tracker,
    mock_organization,
    mock_project,
):
    """
    Regression test: Ensures that organization/project identifiers are correctly handled
    as strings and that the `identifier` field is present in transformed project data.
    """
    with patch("preloop.sync.trackers.github.GitHubTracker"):
        mock_internal_client = AsyncMock()
        # Simulate API response with integer IDs
        mock_internal_client.get_organizations.return_value = [
            {"id": 123, "name": "Test Org"}
        ]
        mock_internal_client.get_projects.return_value = [
            {"id": 456, "name": "Test Project"}
        ]
        mock_internal_client.get_issues.return_value = [
            {"id": "1", "updated_at": "2025-01-01T00:00:00Z"}
        ]

        # Mock the transform methods to ensure they behave as expected
        mock_internal_client.transform_organization = MagicMock(
            return_value={"identifier": "123", "name": "Test Org"}
        )
        mock_internal_client.transform_project = MagicMock(
            return_value={
                "identifier": "456",
                "name": "Test Project",
                "slug": "test-project",
            }
        )
        mock_internal_client.transform_issue = MagicMock(
            return_value={
                "external_id": "1",
                "updated_at": datetime.datetime(
                    2025, 1, 1, tzinfo=datetime.timezone.utc
                ),
                "comments": [],
            }
        )

        client = TrackerClient(mock_tracker)
        client.client = mock_internal_client

        mock_crud_issue.get_by_external_id.return_value = None
        mock_crud_issue.create.return_value = Issue(id=1)

        # Act
        await client.scan_issues(mock_db_session, mock_organization, mock_project)

        # Assert
        # Verify that get_issues was called with string identifiers
        mock_internal_client.get_issues.assert_called_with(
            organization_id="test-org",
            project_id="test-project",
            since=None,
        )
        mock_crud_issue.create.assert_called_once()


@pytest.mark.asyncio
@patch("preloop.sync.scanner.core.crud_comment")
@patch("preloop.sync.scanner.core.crud_issue")
async def test_scan_issues_handles_comments_correctly(
    mock_crud_issue,
    mock_crud_comment,
    mock_db_session,
    mock_tracker,
    mock_organization,
    mock_project,
):
    """
    Regression test: Ensures that comments are processed correctly without errors.
    """
    with patch("preloop.sync.trackers.github.GitHubTracker"):
        mock_internal_client = AsyncMock()
        mock_internal_client.get_issues.return_value = [
            {"id": "1", "updated_at": "2025-01-01T00:00:00Z"}
        ]
        mock_internal_client.transform_issue = MagicMock(
            return_value={
                "external_id": "1",
                "updated_at": datetime.datetime(
                    2025, 1, 1, tzinfo=datetime.timezone.utc
                ),
                "comments": [{"id": "c1", "body": "a comment"}],
            }
        )
        mock_internal_client.transform_comment = MagicMock(
            return_value={
                "external_id": "c1",
                "body": "a comment",
                "updated_at": datetime.datetime(
                    2025, 1, 1, tzinfo=datetime.timezone.utc
                ),
            }
        )

        client = TrackerClient(mock_tracker)
        client.client = mock_internal_client

        mock_issue = Issue(
            id=1,
            external_id="1",
            updated_at=datetime.datetime(2024, 12, 31, tzinfo=datetime.timezone.utc),
            title="Test Title",
            description="Test Description",
        )
        mock_crud_issue.get_by_external_id.return_value = mock_issue
        mock_crud_issue.update.return_value = mock_issue
        mock_crud_comment.get_by_external_id.return_value = None

        created_comment = Comment(
            id="c1_db_id", issue_id=mock_issue.id, body="a comment"
        )
        mock_crud_comment.create.return_value = created_comment
        mock_db_session.get.side_effect = [created_comment, mock_issue]

        # Act
        await client.scan_issues(mock_db_session, mock_organization, mock_project)

        # Assert
        mock_crud_comment.create.assert_called_once()
