# #region Test.Api.GitMergeRoutes [C:3] [TYPE Module] [SEMANTICS test,git,merge,routes]
# @BRIEF Tests for Git merge API routes — status, conflicts, resolve, abort, continue.
# @RELATION BINDS_TO -> [GitMergeRoutes]
# @TEST_EDGE: success -> 200 with expected data
# @TEST_EDGE: unexpected_error -> 500
# @TEST_EDGE: dashboard_not_found -> 404 propagated from resolve_dashboard

import os

os.environ.setdefault("DATABASE_URL", "sqlite:///:memory:")
os.environ.setdefault("AUTH_DATABASE_URL", "sqlite:///:memory:")
os.environ.setdefault("SECRET_KEY", "test-secret-key-for-tests")

import sys
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from fastapi import FastAPI, HTTPException
from fastapi.testclient import TestClient

_src = str(Path(__file__).resolve().parent.parent.parent / "src")
if _src not in sys.path:
    sys.path.insert(0, _src)


MERGE_STATUS = {
    "has_unfinished_merge": True,
    "repository_path": "/tmp/repo",
    "git_dir": "/tmp/repo/.git",
    "current_branch": "dev",
    "merge_head": "abc123",
    "merge_message_preview": "Merge branch 'feature' into dev",
    "conflicts_count": 2,
}

MERGE_CONFLICTS = [
    {"file_path": "dashboard.json", "mine": "my content", "theirs": "their content"},
    {"file_path": "config.yaml", "mine": "my config", "theirs": "their config"},
]


def _make_client() -> TestClient:
    from src.api.routes.git._router import router as parent_router
    from src.core.database import get_db
    from src.dependencies import get_current_user, has_permission
    from src.schemas.auth import User, RoleSchema

    app = FastAPI()
    app.include_router(parent_router)

    mock_user = User(
        id="admin-1", username="admin", email="admin@x.com",
        auth_source="LOCAL",
        created_at=__import__("datetime").datetime.now(),
        roles=[RoleSchema(id="r1", name="Admin", description="", permissions=[])],
    )

    app.dependency_overrides = {
        get_db: lambda: MagicMock(),
        get_current_user: lambda: mock_user,
    }
    return TestClient(app)


class TestGetMergeStatus:
    """GET /repositories/{dashboard_ref}/merge/status"""

    def test_success(self):
        mock_gs = MagicMock()
        mock_gs.get_merge_status.return_value = MERGE_STATUS

        with (
            patch("src.api.routes.git._merge_routes.get_git_service", return_value=mock_gs),
            patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(return_value=42)),
        ):
            client = _make_client()
            resp = client.get("/repositories/42/merge/status")
        assert resp.status_code == 200
        data = resp.json()
        assert data["has_unfinished_merge"] is True
        assert data["current_branch"] == "dev"

    def test_unexpected_error(self):
        mock_gs = MagicMock()
        mock_gs.get_merge_status.side_effect = RuntimeError("boom")

        with (
            patch("src.api.routes.git._merge_routes.get_git_service", return_value=mock_gs),
            patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(return_value=42)),
        ):
            client = _make_client()
            resp = client.get("/repositories/42/merge/status")
        assert resp.status_code == 500
        assert "boom" in resp.text

    def test_dashboard_not_found(self):
        with (
            patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(side_effect=HTTPException(status_code=404, detail="Not found"))),
        ):
            client = _make_client()
            resp = client.get("/repositories/bad-ref/merge/status")
        assert resp.status_code == 404


class TestGetMergeConflicts:
    """GET /repositories/{dashboard_ref}/merge/conflicts"""

    def test_success(self):
        mock_gs = MagicMock()
        mock_gs.get_merge_conflicts.return_value = MERGE_CONFLICTS

        with (
            patch("src.api.routes.git._merge_routes.get_git_service", return_value=mock_gs),
            patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(return_value=42)),
        ):
            client = _make_client()
            resp = client.get("/repositories/42/merge/conflicts")
        assert resp.status_code == 200
        data = resp.json()
        assert len(data) == 2
        assert data[0]["file_path"] == "dashboard.json"

    def test_unexpected_error(self):
        mock_gs = MagicMock()
        mock_gs.get_merge_conflicts.side_effect = RuntimeError("boom")

        with (
            patch("src.api.routes.git._merge_routes.get_git_service", return_value=mock_gs),
            patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(return_value=42)),
        ):
            client = _make_client()
            resp = client.get("/repositories/42/merge/conflicts")
        assert resp.status_code == 500


class TestResolveMergeConflicts:
    """POST /repositories/{dashboard_ref}/merge/resolve"""

    RESOLVE_PAYLOAD = {
        "resolutions": [
            {"file_path": "dashboard.json", "resolution": "mine"},
        ]
    }

    def test_success(self):
        mock_gs = MagicMock()
        mock_gs.resolve_merge_conflicts.return_value = ["dashboard.json"]

        with (
            patch("src.api.routes.git._merge_routes.get_git_service", return_value=mock_gs),
            patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(return_value=42)),
        ):
            client = _make_client()
            resp = client.post("/repositories/42/merge/resolve", json=self.RESOLVE_PAYLOAD)
        assert resp.status_code == 200
        data = resp.json()
        assert data["status"] == "success"
        assert data["resolved_files"] == ["dashboard.json"]

    def test_unexpected_error(self):
        mock_gs = MagicMock()
        mock_gs.resolve_merge_conflicts.side_effect = RuntimeError("boom")

        with (
            patch("src.api.routes.git._merge_routes.get_git_service", return_value=mock_gs),
            patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(return_value=42)),
        ):
            client = _make_client()
            resp = client.post("/repositories/42/merge/resolve", json=self.RESOLVE_PAYLOAD)
        assert resp.status_code == 500


class TestAbortMerge:
    """POST /repositories/{dashboard_ref}/merge/abort"""

    def test_success(self):
        mock_gs = MagicMock()
        mock_gs.abort_merge.return_value = {"status": "aborted"}

        with (
            patch("src.api.routes.git._merge_routes.get_git_service", return_value=mock_gs),
            patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(return_value=42)),
        ):
            client = _make_client()
            resp = client.post("/repositories/42/merge/abort")
        assert resp.status_code == 200

    def test_unexpected_error(self):
        mock_gs = MagicMock()
        mock_gs.abort_merge.side_effect = RuntimeError("boom")

        with (
            patch("src.api.routes.git._merge_routes.get_git_service", return_value=mock_gs),
            patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(return_value=42)),
        ):
            client = _make_client()
            resp = client.post("/repositories/42/merge/abort")
        assert resp.status_code == 500


class TestContinueMerge:
    """POST /repositories/{dashboard_ref}/merge/continue"""

    def test_success(self):
        mock_gs = MagicMock()
        mock_gs.continue_merge.return_value = {"status": "merged"}

        with (
            patch("src.api.routes.git._merge_routes.get_git_service", return_value=mock_gs),
            patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(return_value=42)),
        ):
            client = _make_client()
            resp = client.post("/repositories/42/merge/continue", json={"message": "Merge complete"})
        assert resp.status_code == 200

    def test_success_no_message(self):
        mock_gs = MagicMock()
        mock_gs.continue_merge.return_value = {"status": "merged"}

        with (
            patch("src.api.routes.git._merge_routes.get_git_service", return_value=mock_gs),
            patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(return_value=42)),
        ):
            client = _make_client()
            resp = client.post("/repositories/42/merge/continue", json={})
        assert resp.status_code == 200

    def test_unexpected_error(self):
        mock_gs = MagicMock()
        mock_gs.continue_merge.side_effect = RuntimeError("boom")

        with (
            patch("src.api.routes.git._merge_routes.get_git_service", return_value=mock_gs),
            patch("src.api.routes.git._resolve_dashboard_id_from_ref", AsyncMock(return_value=42)),
        ):
            client = _make_client()
            resp = client.post("/repositories/42/merge/continue", json={"message": "x"})
        assert resp.status_code == 500
# #endregion Test.Api.GitMergeRoutes
