# #region GitRepoOperationsRoutes [C:3] [TYPE Module] [SEMANTICS fastapi, git, api, history, diff]
# @BRIEF FastAPI endpoints for Git repository operations (commit, push, pull, status, diff, history).
# @LAYER API


from fastapi import Depends, HTTPException
from sqlalchemy.orm import Session

from src.api.routes.git_schemas import (
    CommitCreate,
    CommitSchema,
    RepoStatusBatchRequest,
    RepoStatusBatchResponse,
)
from src.core.database import get_db
from src.core.logger import belief_scope, logger
from src.dependencies import get_config_manager, get_current_user, has_permission
from src.models.auth import User
from src.models.git import GitRepository, GitServerConfig

from ._deps import MAX_REPOSITORY_STATUS_BATCH, get_git_service
from ._helpers import (
    _apply_git_identity_from_profile,
    _build_no_repo_status_payload,
    _handle_unexpected_git_route_error,
    _resolve_repository_status,
)
from ._router import router


# #region commit_changes [C:2] [TYPE Function]
# @BRIEF Stage and commit changes in the dashboard's repository.
@router.post("/repositories/{dashboard_ref}/commit")
async def commit_changes(
    dashboard_ref: str,
    commit_data: CommitCreate,
    env_id: str | None = None,
    config_manager=Depends(get_config_manager),
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
    _=Depends(has_permission("plugin:git", "EXECUTE")),
):
    _gs = get_git_service()
    with belief_scope("commit_changes"):
        from . import _resolve_dashboard_id_from_ref

        try:
            dashboard_id = _resolve_dashboard_id_from_ref(
                dashboard_ref, config_manager, env_id
            )
            _apply_git_identity_from_profile(dashboard_id, db, current_user)
            _gs.commit_changes(
                dashboard_id, commit_data.message, commit_data.files
            )
            return {"status": "success"}
        except HTTPException:
            raise
        except Exception as e:
            _handle_unexpected_git_route_error("commit_changes", e)
# #endregion commit_changes


# #region push_changes [C:2] [TYPE Function]
# @BRIEF Push local commits to the remote repository.
@router.post("/repositories/{dashboard_ref}/push")
async def push_changes(
    dashboard_ref: str,
    env_id: str | None = None,
    config_manager=Depends(get_config_manager),
    _=Depends(has_permission("plugin:git", "EXECUTE")),
):
    _gs = get_git_service()
    with belief_scope("push_changes"):
        from . import _resolve_dashboard_id_from_ref

        try:
            dashboard_id = _resolve_dashboard_id_from_ref(
                dashboard_ref, config_manager, env_id
            )
            _gs.push_changes(dashboard_id)
            return {"status": "success"}
        except HTTPException:
            raise
        except Exception as e:
            _handle_unexpected_git_route_error("push_changes", e)
# #endregion push_changes


# #region pull_changes [C:3] [TYPE Function]
# @BRIEF Pull changes from the remote repository.
# @RELATION CALLS -> [GitService]
@router.post("/repositories/{dashboard_ref}/pull")
async def pull_changes(
    dashboard_ref: str,
    env_id: str | None = None,
    config_manager=Depends(get_config_manager),
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user),
    _=Depends(has_permission("plugin:git", "EXECUTE")),
):
    _gs = get_git_service()
    with belief_scope("pull_changes"):
        from . import _resolve_dashboard_id_from_ref

        try:
            dashboard_id = _resolve_dashboard_id_from_ref(
                dashboard_ref, config_manager, env_id
            )
            db_repo = None
            config_url = None
            config_provider = None
            try:
                db_repo_candidate = (
                    db.query(GitRepository)
                    .filter(GitRepository.dashboard_id == dashboard_id)
                    .first()
                )
                if getattr(db_repo_candidate, "config_id", None):
                    db_repo = db_repo_candidate
                    config_row = (
                        db.query(GitServerConfig)
                        .filter(GitServerConfig.id == db_repo.config_id)
                        .first()
                    )
                    if config_row:
                        config_url = config_row.url
                        config_provider = config_row.provider
            except Exception as diagnostics_error:
                logger.explore(
                    "Failed to load repository binding diagnostics",
                    extra={"src": "pull_changes", "payload": {"dashboard_id": dashboard_id}, "error": str(diagnostics_error)},
                )
            logger.reason(
                f"Route diagnostics dashboard_ref={dashboard_ref} env_id={env_id} resolved_dashboard_id={dashboard_id} "
                f"binding_exists={bool(db_repo)} binding_local_path={(db_repo.local_path if db_repo else None)} "
                f"binding_remote_url={(db_repo.remote_url if db_repo else None)} "
                f"binding_config_id={(db_repo.config_id if db_repo else None)} "
                f"config_provider={config_provider} config_url={config_url}",
                extra={"src": "pull_changes"},
            )
            _apply_git_identity_from_profile(dashboard_id, db, current_user)
            _gs.pull_changes(dashboard_id)
            return {"status": "success"}
        except HTTPException:
            raise
        except Exception as e:
            _handle_unexpected_git_route_error("pull_changes", e)
# #endregion pull_changes


# #region get_repository_status [C:2] [TYPE Function]
# @BRIEF Get current Git status for a dashboard repository.
@router.get("/repositories/{dashboard_ref}/status")
async def get_repository_status(
    dashboard_ref: str,
    env_id: str | None = None,
    config_manager=Depends(get_config_manager),
    _=Depends(has_permission("plugin:git", "EXECUTE")),
):
    with belief_scope("get_repository_status"):
        from . import _resolve_dashboard_id_from_ref

        try:
            dashboard_id = _resolve_dashboard_id_from_ref(
                dashboard_ref, config_manager, env_id
            )
            return _resolve_repository_status(dashboard_id)
        except HTTPException:
            raise
        except Exception as e:
            _handle_unexpected_git_route_error("get_repository_status", e)
# #endregion get_repository_status


# #region get_repository_status_batch [C:2] [TYPE Function]
# @BRIEF Get Git statuses for multiple dashboard repositories in one request.
@router.post("/repositories/status/batch", response_model=RepoStatusBatchResponse)
async def get_repository_status_batch(
    request: RepoStatusBatchRequest,
    _=Depends(has_permission("plugin:git", "EXECUTE")),
):
    with belief_scope("get_repository_status_batch"):
        dashboard_ids = list(dict.fromkeys(request.dashboard_ids))
        if len(dashboard_ids) > MAX_REPOSITORY_STATUS_BATCH:
            logger.reason(
                f"Batch size {len(dashboard_ids)} exceeds limit {MAX_REPOSITORY_STATUS_BATCH}. Truncating request.",
                extra={"src": "get_repository_status_batch"},
            )
            dashboard_ids = dashboard_ids[:MAX_REPOSITORY_STATUS_BATCH]

        statuses = {}
        for dashboard_id in dashboard_ids:
            try:
                statuses[str(dashboard_id)] = _resolve_repository_status(dashboard_id)
            except HTTPException:
                statuses[str(dashboard_id)] = {
                    **_build_no_repo_status_payload(),
                    "sync_state": "ERROR",
                    "sync_status": "ERROR",
                }
            except Exception as e:
                logger.error(
                    f"[get_repository_status_batch][Coherence:Failed] Failed for dashboard {dashboard_id}: {e}"
                )
                statuses[str(dashboard_id)] = {
                    **_build_no_repo_status_payload(),
                    "sync_state": "ERROR",
                    "sync_status": "ERROR",
                }
        return RepoStatusBatchResponse(statuses=statuses)
# #endregion get_repository_status_batch


# #region get_repository_diff [C:2] [TYPE Function]
# @BRIEF Get Git diff for a dashboard repository.
@router.get("/repositories/{dashboard_ref}/diff")
async def get_repository_diff(
    dashboard_ref: str,
    file_path: str | None = None,
    staged: bool = False,
    env_id: str | None = None,
    config_manager=Depends(get_config_manager),
    _=Depends(has_permission("plugin:git", "EXECUTE")),
):
    _gs = get_git_service()
    with belief_scope("get_repository_diff"):
        from . import _resolve_dashboard_id_from_ref

        try:
            dashboard_id = _resolve_dashboard_id_from_ref(
                dashboard_ref, config_manager, env_id
            )
            return _gs.get_diff(dashboard_id, file_path, staged)
        except HTTPException:
            raise
        except Exception as e:
            _handle_unexpected_git_route_error("get_repository_diff", e)
# #endregion get_repository_diff


# #region get_history [C:2] [TYPE Function]
# @BRIEF View commit history for a dashboard's repository.
@router.get("/repositories/{dashboard_ref}/history", response_model=list[CommitSchema])
async def get_history(
    dashboard_ref: str,
    limit: int = 50,
    env_id: str | None = None,
    config_manager=Depends(get_config_manager),
    _=Depends(has_permission("plugin:git", "EXECUTE")),
):
    _gs = get_git_service()
    with belief_scope("get_history"):
        from . import _resolve_dashboard_id_from_ref

        try:
            dashboard_id = _resolve_dashboard_id_from_ref(
                dashboard_ref, config_manager, env_id
            )
            return _gs.get_commit_history(dashboard_id, limit)
        except HTTPException:
            raise
        except Exception as e:
            _handle_unexpected_git_route_error("get_history", e)
# #endregion get_history


# #region generate_commit_message [C:3] [TYPE Function]
# @BRIEF Generate a suggested commit message using LLM.
# @RELATION CALLS -> [GitService]
@router.post("/repositories/{dashboard_ref}/generate-message")
async def generate_commit_message(
    dashboard_ref: str,
    env_id: str | None = None,
    config_manager=Depends(get_config_manager),
    db: Session = Depends(get_db),
    _=Depends(has_permission("plugin:git", "EXECUTE")),
):
    _gs = get_git_service()
    with belief_scope("generate_commit_message"):
        from . import _resolve_dashboard_id_from_ref

        try:
            dashboard_id = _resolve_dashboard_id_from_ref(
                dashboard_ref, config_manager, env_id
            )
            diff = _gs.get_diff(dashboard_id, staged=True)
            if not diff:
                diff = _gs.get_diff(dashboard_id, staged=False)

            if not diff:
                return {"message": "No changes detected"}

            history_objs = _gs.get_commit_history(dashboard_id, limit=5)
            history = [h.message for h in history_objs if hasattr(h, "message")]

            from src.plugins.llm_analysis.models import LLMProviderType
            from src.plugins.llm_analysis.service import LLMClient
            from src.services.llm_prompt_templates import (
                DEFAULT_LLM_PROMPTS,
                normalize_llm_settings,
                resolve_bound_provider_id,
            )
            from src.services.llm_provider import LLMProviderService

            llm_service = LLMProviderService(db)
            providers = llm_service.get_all_providers()
            llm_settings = normalize_llm_settings(
                config_manager.get_config().settings.llm
            )
            bound_provider_id = resolve_bound_provider_id(llm_settings, "git_commit")
            provider = next((p for p in providers if p.id == bound_provider_id), None)
            if not provider:
                provider = next((p for p in providers if p.is_active), None)

            if not provider:
                raise HTTPException(
                    status_code=400, detail="No active LLM provider found"
                )

            api_key = llm_service.get_decrypted_api_key(provider.id)
            client = LLMClient(
                provider_type=LLMProviderType(provider.provider_type),
                api_key=api_key,
                base_url=provider.base_url,
                default_model=provider.default_model,
            )

            from src.plugins.git.llm_extension import GitLLMExtension

            extension = GitLLMExtension(client)
            git_prompt = llm_settings["prompts"].get(
                "git_commit_prompt",
                DEFAULT_LLM_PROMPTS["git_commit_prompt"],
            )
            message = await extension.suggest_commit_message(
                diff,
                history,
                prompt_template=git_prompt,
            )
            return {"message": message}
        except HTTPException:
            raise
        except Exception as e:
            _handle_unexpected_git_route_error("generate_commit_message", e)
# #endregion generate_commit_message
# #endregion GitRepoOperationsRoutes
