# [DEF:backend.src.api.routes.__tests__.test_git_status_route:Module]
# @TIER: STANDARD
# @SEMANTICS: tests, git, api, status, no_repo
# @PURPOSE: Validate status endpoint behavior for missing and error repository states.
# @LAYER: Domain (Tests)
# @RELATION: CALLS -> src.api.routes.git.get_repository_status

from fastapi import HTTPException
import pytest
import asyncio

from src.api.routes import git as git_routes


# [DEF:test_get_repository_status_returns_no_repo_payload_for_missing_repo:Function]
# @PURPOSE: Ensure missing local repository is represented as NO_REPO payload instead of an API error.
# @PRE: GitService.get_status raises HTTPException(404).
# @POST: Route returns a deterministic NO_REPO status payload.
def test_get_repository_status_returns_no_repo_payload_for_missing_repo(monkeypatch):
    class MissingRepoGitService:
        def _get_repo_path(self, dashboard_id: int) -> str:
            return f"/tmp/missing-repo-{dashboard_id}"

        def get_status(self, dashboard_id: int) -> dict:
            raise AssertionError("get_status must not be called when repository path is missing")

    monkeypatch.setattr(git_routes, "git_service", MissingRepoGitService())

    response = asyncio.run(git_routes.get_repository_status(34))

    assert response["sync_status"] == "NO_REPO"
    assert response["sync_state"] == "NO_REPO"
    assert response["has_repo"] is False
    assert response["current_branch"] is None
# [/DEF:test_get_repository_status_returns_no_repo_payload_for_missing_repo:Function]


# [DEF:test_get_repository_status_propagates_non_404_http_exception:Function]
# @PURPOSE: Ensure HTTP exceptions other than 404 are not masked.
# @PRE: GitService.get_status raises HTTPException with non-404 status.
# @POST: Raised exception preserves original status and detail.
def test_get_repository_status_propagates_non_404_http_exception(monkeypatch):
    class ConflictGitService:
        def _get_repo_path(self, dashboard_id: int) -> str:
            return f"/tmp/existing-repo-{dashboard_id}"

        def get_status(self, dashboard_id: int) -> dict:
            raise HTTPException(status_code=409, detail="Conflict")

    monkeypatch.setattr(git_routes, "git_service", ConflictGitService())
    monkeypatch.setattr(git_routes.os.path, "exists", lambda _path: True)

    with pytest.raises(HTTPException) as exc_info:
        asyncio.run(git_routes.get_repository_status(34))

    assert exc_info.value.status_code == 409
    assert exc_info.value.detail == "Conflict"
# [/DEF:test_get_repository_status_propagates_non_404_http_exception:Function]


# [DEF:test_get_repository_diff_propagates_http_exception:Function]
# @PURPOSE: Ensure diff endpoint preserves domain HTTP errors from GitService.
# @PRE: GitService.get_diff raises HTTPException.
# @POST: Endpoint raises same HTTPException values.
def test_get_repository_diff_propagates_http_exception(monkeypatch):
    class DiffGitService:
        def get_diff(self, dashboard_id: int, file_path=None, staged: bool = False) -> str:
            raise HTTPException(status_code=404, detail="Repository missing")

    monkeypatch.setattr(git_routes, "git_service", DiffGitService())

    with pytest.raises(HTTPException) as exc_info:
        asyncio.run(git_routes.get_repository_diff(12))

    assert exc_info.value.status_code == 404
    assert exc_info.value.detail == "Repository missing"
# [/DEF:test_get_repository_diff_propagates_http_exception:Function]


# [DEF:test_get_history_wraps_unexpected_error_as_500:Function]
# @PURPOSE: Ensure non-HTTP exceptions in history endpoint become deterministic 500 errors.
# @PRE: GitService.get_commit_history raises ValueError.
# @POST: Endpoint returns HTTPException with status 500 and route context.
def test_get_history_wraps_unexpected_error_as_500(monkeypatch):
    class HistoryGitService:
        def get_commit_history(self, dashboard_id: int, limit: int = 50):
            raise ValueError("broken parser")

    monkeypatch.setattr(git_routes, "git_service", HistoryGitService())

    with pytest.raises(HTTPException) as exc_info:
        asyncio.run(git_routes.get_history(12))

    assert exc_info.value.status_code == 500
    assert exc_info.value.detail == "get_history failed: broken parser"
# [/DEF:test_get_history_wraps_unexpected_error_as_500:Function]


# [DEF:test_commit_changes_wraps_unexpected_error_as_500:Function]
# @PURPOSE: Ensure commit endpoint does not leak unexpected errors as 400.
# @PRE: GitService.commit_changes raises RuntimeError.
# @POST: Endpoint raises HTTPException(500) with route context.
def test_commit_changes_wraps_unexpected_error_as_500(monkeypatch):
    class CommitGitService:
        def commit_changes(self, dashboard_id: int, message: str, files):
            raise RuntimeError("index lock")

    class CommitPayload:
        message = "test"
        files = ["dashboards/a.yaml"]

    monkeypatch.setattr(git_routes, "git_service", CommitGitService())

    with pytest.raises(HTTPException) as exc_info:
        asyncio.run(git_routes.commit_changes(12, CommitPayload()))

    assert exc_info.value.status_code == 500
    assert exc_info.value.detail == "commit_changes failed: index lock"
# [/DEF:test_commit_changes_wraps_unexpected_error_as_500:Function]


# [DEF:test_get_repository_status_batch_returns_mixed_statuses:Function]
# @PURPOSE: Ensure batch endpoint returns per-dashboard statuses in one response.
# @PRE: Some repositories are missing and some are initialized.
# @POST: Returned map includes resolved status for each requested dashboard ID.
def test_get_repository_status_batch_returns_mixed_statuses(monkeypatch):
    class BatchGitService:
        def _get_repo_path(self, dashboard_id: int) -> str:
            return f"/tmp/repo-{dashboard_id}"

        def get_status(self, dashboard_id: int) -> dict:
            if dashboard_id == 2:
                return {"sync_state": "SYNCED", "sync_status": "OK"}
            raise HTTPException(status_code=404, detail="not found")

    monkeypatch.setattr(git_routes, "git_service", BatchGitService())
    monkeypatch.setattr(git_routes.os.path, "exists", lambda path: path.endswith("/repo-2"))

    class BatchRequest:
        dashboard_ids = [1, 2]

    response = asyncio.run(git_routes.get_repository_status_batch(BatchRequest()))

    assert response.statuses["1"]["sync_status"] == "NO_REPO"
    assert response.statuses["2"]["sync_state"] == "SYNCED"
# [/DEF:test_get_repository_status_batch_returns_mixed_statuses:Function]


# [DEF:test_get_repository_status_batch_marks_item_as_error_on_service_failure:Function]
# @PURPOSE: Ensure batch endpoint marks failed items as ERROR without failing entire request.
# @PRE: GitService raises non-HTTP exception for one dashboard.
# @POST: Failed dashboard status is marked as ERROR.
def test_get_repository_status_batch_marks_item_as_error_on_service_failure(monkeypatch):
    class BatchErrorGitService:
        def _get_repo_path(self, dashboard_id: int) -> str:
            return f"/tmp/repo-{dashboard_id}"

        def get_status(self, dashboard_id: int) -> dict:
            raise RuntimeError("boom")

    monkeypatch.setattr(git_routes, "git_service", BatchErrorGitService())
    monkeypatch.setattr(git_routes.os.path, "exists", lambda _path: True)

    class BatchRequest:
        dashboard_ids = [9]

    response = asyncio.run(git_routes.get_repository_status_batch(BatchRequest()))

    assert response.statuses["9"]["sync_status"] == "ERROR"
    assert response.statuses["9"]["sync_state"] == "ERROR"
# [/DEF:test_get_repository_status_batch_marks_item_as_error_on_service_failure:Function]


# [DEF:test_get_repository_status_batch_deduplicates_and_truncates_ids:Function]
# @PURPOSE: Ensure batch endpoint protects server from oversized payloads.
# @PRE: request includes duplicate IDs and more than MAX_REPOSITORY_STATUS_BATCH entries.
# @POST: Result contains unique IDs up to configured cap.
def test_get_repository_status_batch_deduplicates_and_truncates_ids(monkeypatch):
    class SafeBatchGitService:
        def _get_repo_path(self, dashboard_id: int) -> str:
            return f"/tmp/repo-{dashboard_id}"

        def get_status(self, dashboard_id: int) -> dict:
            return {"sync_state": "SYNCED", "sync_status": "OK"}

    monkeypatch.setattr(git_routes, "git_service", SafeBatchGitService())
    monkeypatch.setattr(git_routes.os.path, "exists", lambda _path: True)

    class BatchRequest:
        dashboard_ids = [1, 1] + list(range(2, 90))

    response = asyncio.run(git_routes.get_repository_status_batch(BatchRequest()))

    assert len(response.statuses) == git_routes.MAX_REPOSITORY_STATUS_BATCH
    assert "1" in response.statuses
# [/DEF:test_get_repository_status_batch_deduplicates_and_truncates_ids:Function]

# [/DEF:backend.src.api.routes.__tests__.test_git_status_route:Module]
