# #region GitHelpers [C:3] [TYPE Module] [SEMANTICS fastapi, git, api]
# @BRIEF Shared helper functions for Git route modules.
# @LAYER API
# @RELATION CALLS -> [GitDeps]
# @RELATION CALLS -> [SupersetClient]
# @RELATION CALLS -> [UserDashboardPreference]

import os

from fastapi import HTTPException
from sqlalchemy.orm import Session

from src.core.logger import logger
from src.core.superset_client import SupersetClient
from src.models.auth import User
from src.models.git import GitServerConfig
from src.models.profile import UserDashboardPreference

from ._deps import get_git_service


# #region _build_no_repo_status_payload [C:1] [TYPE Function]
# @BRIEF Build a consistent status payload for dashboards without initialized repositories.
# @POST  Returns a stable payload compatible with frontend repository status parsing.
def _build_no_repo_status_payload() -> dict:
    return {
        "is_dirty": False,
        "untracked_files": [],
        "modified_files": [],
        "staged_files": [],
        "current_branch": None,
        "upstream_branch": None,
        "has_upstream": False,
        "ahead_count": 0,
        "behind_count": 0,
        "is_diverged": False,
        "sync_state": "NO_REPO",
        "sync_status": "NO_REPO",
        "has_repo": False,
    }
# #endregion _build_no_repo_status_payload


# #region _handle_unexpected_git_route_error [C:1] [TYPE Function]
# @BRIEF Convert unexpected route-level exceptions to stable 500 API responses.
# @PRE  `error` is a non-HTTPException instance.
# @POST  Raises HTTPException(500) with route-specific context.
def _handle_unexpected_git_route_error(route_name: str, error: Exception) -> None:
    logger.error(f"[{route_name}][Coherence:Failed] {error}")
    raise HTTPException(status_code=500, detail=f"{route_name} failed: {error!s}")
# #endregion _handle_unexpected_git_route_error


# #region _resolve_repository_status [C:2] [TYPE Function]
# @BRIEF Resolve repository status for one dashboard with graceful NO_REPO semantics.
# @PRE  `dashboard_id` is a valid integer.
# @POST  Returns standard status payload or `NO_REPO` payload when repository path is absent.
def _resolve_repository_status(dashboard_id: int) -> dict:
    git_service = get_git_service()
    repo_path = git_service._get_repo_path(dashboard_id)
    if not os.path.exists(repo_path):
        logger.reason(
            f"Repository is not initialized for dashboard {dashboard_id}",
            extra={"src": "get_repository_status"},
        )
        return _build_no_repo_status_payload()

    try:
        return git_service.get_status(dashboard_id)
    except HTTPException as e:
        if e.status_code == 404:
            logger.reason(
                f"Repository is not initialized for dashboard {dashboard_id}",
                extra={"src": "get_repository_status"},
            )
            return _build_no_repo_status_payload()
        raise
# #endregion _resolve_repository_status


# #region _get_git_config_or_404 [C:2] [TYPE Function]
# @BRIEF Resolve GitServerConfig by id or raise 404.
def _get_git_config_or_404(db: Session, config_id: str) -> GitServerConfig:
    config = db.query(GitServerConfig).filter(GitServerConfig.id == config_id).first()
    if not config:
        raise HTTPException(status_code=404, detail="Git configuration not found")
    return config
# #endregion _get_git_config_or_404


# #region _find_dashboard_id_by_slug [C:2] [TYPE Function]
# @BRIEF Resolve dashboard numeric ID by slug in a specific environment.
def _find_dashboard_id_by_slug(
    client: SupersetClient,
    dashboard_slug: str,
) -> int | None:
    query_variants = [
        {
            "filters": [{"col": "slug", "opr": "eq", "value": dashboard_slug}],
            "page": 0,
            "page_size": 1,
        },
        {
            "filters": [{"col": "slug", "op": "eq", "value": dashboard_slug}],
            "page": 0,
            "page_size": 1,
        },
    ]

    for query in query_variants:
        try:
            _count, dashboards = client.get_dashboards_page(query=query)
            if dashboards:
                resolved_id = dashboards[0].get("id")
                if resolved_id is not None:
                    return int(resolved_id)
        except Exception:
            continue
    return None
# #endregion _find_dashboard_id_by_slug


# #region _resolve_dashboard_id_from_ref [C:2] [TYPE Function]
# @BRIEF Resolve dashboard ID from slug-or-id reference for Git routes.
def _resolve_dashboard_id_from_ref(
    dashboard_ref: str,
    config_manager,
    env_id: str | None = None,
) -> int:
    normalized_ref = str(dashboard_ref or "").strip()
    if not normalized_ref:
        raise HTTPException(status_code=400, detail="dashboard_ref is required")

    if normalized_ref.isdigit():
        return int(normalized_ref)

    if not env_id:
        raise HTTPException(
            status_code=400,
            detail="env_id is required for slug-based Git operations",
        )

    environments = config_manager.get_environments()
    env = next((e for e in environments if e.id == env_id), None)
    if not env:
        raise HTTPException(status_code=404, detail="Environment not found")

    dashboard_id = _find_dashboard_id_by_slug(SupersetClient(env), normalized_ref)
    if dashboard_id is None:
        raise HTTPException(
            status_code=404, detail=f"Dashboard slug '{normalized_ref}' not found"
        )
    return dashboard_id
# #endregion _resolve_dashboard_id_from_ref


# #region _find_dashboard_id_by_slug_async [C:2] [TYPE Function]
# @BRIEF Resolve dashboard numeric ID by slug asynchronously for hot-path Git routes.
async def _find_dashboard_id_by_slug_async(
    client: "AsyncSupersetClient",
    dashboard_slug: str,
) -> int | None:
    query_variants = [
        {
            "filters": [{"col": "slug", "opr": "eq", "value": dashboard_slug}],
            "page": 0,
            "page_size": 1,
        },
        {
            "filters": [{"col": "slug", "op": "eq", "value": dashboard_slug}],
            "page": 0,
            "page_size": 1,
        },
    ]

    for query in query_variants:
        try:
            _count, dashboards = await client.get_dashboards_page_async(query=query)
            if dashboards:
                resolved_id = dashboards[0].get("id")
                if resolved_id is not None:
                    return int(resolved_id)
        except Exception:
            continue
    return None
# #endregion _find_dashboard_id_by_slug_async


# #region _resolve_dashboard_id_from_ref_async [C:2] [TYPE Function]
# @BRIEF Resolve dashboard ID asynchronously from slug-or-id reference for hot Git routes.
async def _resolve_dashboard_id_from_ref_async(
    dashboard_ref: str,
    config_manager,
    env_id: str | None = None,
) -> int:
    normalized_ref = str(dashboard_ref or "").strip()
    if not normalized_ref:
        raise HTTPException(status_code=400, detail="dashboard_ref is required")

    if normalized_ref.isdigit():
        return int(normalized_ref)

    if not env_id:
        raise HTTPException(
            status_code=400,
            detail="env_id is required for slug-based Git operations",
        )

    environments = config_manager.get_environments()
    env = next((e for e in environments if e.id == env_id), None)
    if not env:
        raise HTTPException(status_code=404, detail="Environment not found")

    from src.core.async_superset_client import AsyncSupersetClient

    client = AsyncSupersetClient(env)
    try:
        dashboard_id = await _find_dashboard_id_by_slug_async(client, normalized_ref)
        if dashboard_id is None:
            raise HTTPException(
                status_code=404, detail=f"Dashboard slug '{normalized_ref}' not found"
            )
        return dashboard_id
    finally:
        await client.aclose()
# #endregion _resolve_dashboard_id_from_ref_async


# #region _resolve_repo_key_from_ref [C:2] [TYPE Function]
# @BRIEF Resolve repository folder key with slug-first strategy and deterministic fallback.
def _resolve_repo_key_from_ref(
    dashboard_ref: str,
    dashboard_id: int,
    config_manager,
    env_id: str | None = None,
) -> str:
    normalized_ref = str(dashboard_ref or "").strip()
    if normalized_ref and not normalized_ref.isdigit():
        return normalized_ref

    if env_id:
        try:
            environments = config_manager.get_environments()
            env = next((e for e in environments if e.id == env_id), None)
            if env:
                payload = SupersetClient(env).get_dashboard(dashboard_id)
                dashboard_data = (
                    payload.get("result", payload) if isinstance(payload, dict) else {}
                )
                dashboard_slug = dashboard_data.get("slug")
                if dashboard_slug:
                    return str(dashboard_slug)
        except Exception:
            logger.debug("Could not resolve dashboard slug for dashboard_id=%s", dashboard_id)

    return f"dashboard-{dashboard_id}"
# #endregion _resolve_repo_key_from_ref


# #region _sanitize_optional_identity_value [C:1] [TYPE Function]
# @BRIEF Normalize optional identity value into trimmed string or None.
def _sanitize_optional_identity_value(value: str | None) -> str | None:
    normalized = str(value or "").strip()
    if not normalized:
        return None
    return normalized
# #endregion _sanitize_optional_identity_value


# #region _resolve_current_user_git_identity [C:2] [TYPE Function]
# @BRIEF Resolve configured Git username/email from current user's profile preferences.
def _resolve_current_user_git_identity(
    db: Session,
    current_user: User | None,
) -> tuple[str, str] | None:
    if db is None or not hasattr(db, "query"):
        return None

    user_id = _sanitize_optional_identity_value(getattr(current_user, "id", None))
    if not user_id:
        return None

    try:
        preference = (
            db.query(UserDashboardPreference)
            .filter(UserDashboardPreference.user_id == user_id)
            .first()
        )
    except Exception as resolve_error:
        logger.explore(
            "Failed to load profile preference for resolving git identity",
            extra={"src": "_resolve_current_user_git_identity", "payload": {"user_id": user_id}, "error": str(resolve_error)},
        )
        return None

    if not preference:
        return None

    git_username = _sanitize_optional_identity_value(
        getattr(preference, "git_username", None)
    )
    git_email = _sanitize_optional_identity_value(
        getattr(preference, "git_email", None)
    )
    if not git_username or not git_email:
        return None
    return git_username, git_email
# #endregion _resolve_current_user_git_identity


# #region _apply_git_identity_from_profile [C:2] [TYPE Function]
# @BRIEF Apply user-scoped Git identity to repository-local config before write/pull operations.
def _apply_git_identity_from_profile(
    dashboard_id: int,
    db: Session,
    current_user: User | None,
) -> None:
    identity = _resolve_current_user_git_identity(db, current_user)
    if not identity:
        return

    git_service = get_git_service()
    configure_identity = getattr(git_service, "configure_identity", None)
    if not callable(configure_identity):
        return

    git_username, git_email = identity
    configure_identity(dashboard_id, git_username, git_email)
# #endregion _apply_git_identity_from_profile
# #endregion GitHelpers
