# [DEF:backend.src.api.routes.git:Module]
#
# @SEMANTICS: git, routes, api, fastapi, repository, deployment
# @PURPOSE:   Provides FastAPI endpoints for Git integration operations.
# @LAYER:     API
# @RELATION:  USES -> src.services.git_service.GitService
# @RELATION:  USES -> src.api.routes.git_schemas
# @RELATION:  USES -> src.models.git
#
# @INVARIANT: All Git operations must be routed through GitService.

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List, Optional
import typing
from src.dependencies import get_config_manager, has_permission
from src.core.database import get_db
from src.models.git import GitServerConfig, GitStatus, DeploymentEnvironment, GitRepository
from src.api.routes.git_schemas import (
    GitServerConfigSchema, GitServerConfigCreate,
    GitRepositorySchema, BranchSchema, BranchCreate,
    BranchCheckout, CommitSchema, CommitCreate,
    DeploymentEnvironmentSchema, DeployRequest, RepoInitRequest
)
from src.services.git_service import GitService
from src.core.logger import logger, belief_scope

router = APIRouter(prefix="/api/git", tags=["git"])
git_service = GitService()

# [DEF:get_git_configs:Function]
# @PURPOSE: List all configured Git servers.
# @PRE: Database session `db` is available.
# @POST: Returns a list of all GitServerConfig objects from the database.
# @RETURN: List[GitServerConfigSchema]
@router.get("/config", response_model=List[GitServerConfigSchema])
async def get_git_configs(
    db: Session = Depends(get_db),
    _ = Depends(has_permission("admin:settings", "READ"))
):
    with belief_scope("get_git_configs"):
        return db.query(GitServerConfig).all()
# [/DEF:get_git_configs:Function]

# [DEF:create_git_config:Function]
# @PURPOSE: Register a new Git server configuration.
# @PRE: `config` contains valid GitServerConfigCreate data.
# @POST: A new GitServerConfig record is created in the database.
# @PARAM: config (GitServerConfigCreate)
# @RETURN: GitServerConfigSchema
@router.post("/config", response_model=GitServerConfigSchema)
async def create_git_config(
    config: GitServerConfigCreate,
    db: Session = Depends(get_db),
    _ = Depends(has_permission("admin:settings", "WRITE"))
):
    with belief_scope("create_git_config"):
        db_config = GitServerConfig(**config.dict())
        db.add(db_config)
        db.commit()
        db.refresh(db_config)
        return db_config
# [/DEF:create_git_config:Function]

# [DEF:delete_git_config:Function]
# @PURPOSE: Remove a Git server configuration.
# @PRE: `config_id` corresponds to an existing configuration.
# @POST: The configuration record is removed from the database.
# @PARAM: config_id (str)
@router.delete("/config/{config_id}")
async def delete_git_config(
    config_id: str,
    db: Session = Depends(get_db),
    _ = Depends(has_permission("admin:settings", "WRITE"))
):
    with belief_scope("delete_git_config"):
        db_config = db.query(GitServerConfig).filter(GitServerConfig.id == config_id).first()
        if not db_config:
            raise HTTPException(status_code=404, detail="Configuration not found")
        
        db.delete(db_config)
        db.commit()
        return {"status": "success", "message": "Configuration deleted"}
# [/DEF:delete_git_config:Function]

# [DEF:test_git_config:Function]
# @PURPOSE: Validate connection to a Git server using provided credentials.
# @PRE: `config` contains provider, url, and pat.
# @POST: Returns success if the connection is validated via GitService.
# @PARAM: config (GitServerConfigCreate)
@router.post("/config/test")
async def test_git_config(
    config: GitServerConfigCreate,
    _ = Depends(has_permission("admin:settings", "READ"))
):
    with belief_scope("test_git_config"):
        success = await git_service.test_connection(config.provider, config.url, config.pat)
        if success:
            return {"status": "success", "message": "Connection successful"}
        else:
            raise HTTPException(status_code=400, detail="Connection failed")
# [/DEF:test_git_config:Function]

# [DEF:init_repository:Function]
# @PURPOSE: Link a dashboard to a Git repository and perform initial clone/init.
# @PRE: `dashboard_id` exists and `init_data` contains valid config_id and remote_url.
# @POST: Repository is initialized on disk and a GitRepository record is saved in DB.
# @PARAM: dashboard_id (int)
# @PARAM: init_data (RepoInitRequest)
@router.post("/repositories/{dashboard_id}/init")
async def init_repository(
    dashboard_id: int,
    init_data: RepoInitRequest,
    db: Session = Depends(get_db),
    _ = Depends(has_permission("plugin:git", "EXECUTE"))
):
    with belief_scope("init_repository"):
        # 1. Get config
        config = db.query(GitServerConfig).filter(GitServerConfig.id == init_data.config_id).first()
        if not config:
            raise HTTPException(status_code=404, detail="Git configuration not found")
        
        try:
            # 2. Perform Git clone/init
            logger.info(f"[init_repository][Action] Initializing repo for dashboard {dashboard_id}")
            git_service.init_repo(dashboard_id, init_data.remote_url, config.pat)
            
            # 3. Save to DB
            repo_path = git_service._get_repo_path(dashboard_id)
            db_repo = db.query(GitRepository).filter(GitRepository.dashboard_id == dashboard_id).first()
            if not db_repo:
                db_repo = GitRepository(
                    dashboard_id=dashboard_id,
                    config_id=config.id,
                    remote_url=init_data.remote_url,
                    local_path=repo_path
                )
                db.add(db_repo)
            else:
                db_repo.config_id = config.id
                db_repo.remote_url = init_data.remote_url
                db_repo.local_path = repo_path
            
            db.commit()
            logger.info(f"[init_repository][Coherence:OK] Repository initialized for dashboard {dashboard_id}")
            return {"status": "success", "message": "Repository initialized"}
        except Exception as e:
            db.rollback()
            logger.error(f"[init_repository][Coherence:Failed] Failed to init repository: {e}")
            raise HTTPException(status_code=400, detail=str(e))
# [/DEF:init_repository:Function]

# [DEF:get_branches:Function]
# @PURPOSE: List all branches for a dashboard's repository.
# @PRE: Repository for `dashboard_id` is initialized.
# @POST: Returns a list of branches from the local repository.
# @PARAM: dashboard_id (int)
# @RETURN: List[BranchSchema]
@router.get("/repositories/{dashboard_id}/branches", response_model=List[BranchSchema])
async def get_branches(
    dashboard_id: int,
    _ = Depends(has_permission("plugin:git", "EXECUTE"))
):
    with belief_scope("get_branches"):
        try:
            return git_service.list_branches(dashboard_id)
        except Exception as e:
            raise HTTPException(status_code=404, detail=str(e))
# [/DEF:get_branches:Function]

# [DEF:create_branch:Function]
# @PURPOSE: Create a new branch in the dashboard's repository.
# @PRE: `dashboard_id` repository exists and `branch_data` has name and from_branch.
# @POST: A new branch is created in the local repository.
# @PARAM: dashboard_id (int)
# @PARAM: branch_data (BranchCreate)
@router.post("/repositories/{dashboard_id}/branches")
async def create_branch(
    dashboard_id: int,
    branch_data: BranchCreate,
    _ = Depends(has_permission("plugin:git", "EXECUTE"))
):
    with belief_scope("create_branch"):
        try:
            git_service.create_branch(dashboard_id, branch_data.name, branch_data.from_branch)
            return {"status": "success"}
        except Exception as e:
            raise HTTPException(status_code=400, detail=str(e))
# [/DEF:create_branch:Function]

# [DEF:checkout_branch:Function]
# @PURPOSE: Switch the dashboard's repository to a specific branch.
# @PRE: `dashboard_id` repository exists and branch `checkout_data.name` exists.
# @POST: The local repository HEAD is moved to the specified branch.
# @PARAM: dashboard_id (int)
# @PARAM: checkout_data (BranchCheckout)
@router.post("/repositories/{dashboard_id}/checkout")
async def checkout_branch(
    dashboard_id: int,
    checkout_data: BranchCheckout,
    _ = Depends(has_permission("plugin:git", "EXECUTE"))
):
    with belief_scope("checkout_branch"):
        try:
            git_service.checkout_branch(dashboard_id, checkout_data.name)
            return {"status": "success"}
        except Exception as e:
            raise HTTPException(status_code=400, detail=str(e))
# [/DEF:checkout_branch:Function]

# [DEF:commit_changes:Function]
# @PURPOSE: Stage and commit changes in the dashboard's repository.
# @PRE: `dashboard_id` repository exists and `commit_data` has message and files.
# @POST: Specified files are staged and a new commit is created.
# @PARAM: dashboard_id (int)
# @PARAM: commit_data (CommitCreate)
@router.post("/repositories/{dashboard_id}/commit")
async def commit_changes(
    dashboard_id: int,
    commit_data: CommitCreate,
    _ = Depends(has_permission("plugin:git", "EXECUTE"))
):
    with belief_scope("commit_changes"):
        try:
            git_service.commit_changes(dashboard_id, commit_data.message, commit_data.files)
            return {"status": "success"}
        except Exception as e:
            raise HTTPException(status_code=400, detail=str(e))
# [/DEF:commit_changes:Function]

# [DEF:push_changes:Function]
# @PURPOSE: Push local commits to the remote repository.
# @PRE: `dashboard_id` repository exists and has a remote configured.
# @POST: Local commits are pushed to the remote repository.
# @PARAM: dashboard_id (int)
@router.post("/repositories/{dashboard_id}/push")
async def push_changes(
    dashboard_id: int,
    _ = Depends(has_permission("plugin:git", "EXECUTE"))
):
    with belief_scope("push_changes"):
        try:
            git_service.push_changes(dashboard_id)
            return {"status": "success"}
        except Exception as e:
            raise HTTPException(status_code=400, detail=str(e))
# [/DEF:push_changes:Function]

# [DEF:pull_changes:Function]
# @PURPOSE: Pull changes from the remote repository.
# @PRE: `dashboard_id` repository exists and has a remote configured.
# @POST: Remote changes are fetched and merged into the local branch.
# @PARAM: dashboard_id (int)
@router.post("/repositories/{dashboard_id}/pull")
async def pull_changes(
    dashboard_id: int,
    _ = Depends(has_permission("plugin:git", "EXECUTE"))
):
    with belief_scope("pull_changes"):
        try:
            git_service.pull_changes(dashboard_id)
            return {"status": "success"}
        except Exception as e:
            raise HTTPException(status_code=400, detail=str(e))
# [/DEF:pull_changes:Function]

# [DEF:sync_dashboard:Function]
# @PURPOSE: Sync dashboard state from Superset to Git using the GitPlugin.
# @PRE: `dashboard_id` is valid; GitPlugin is available.
# @POST: Dashboard YAMLs are exported from Superset and committed to Git.
# @PARAM: dashboard_id (int)
# @PARAM: source_env_id (Optional[str])
@router.post("/repositories/{dashboard_id}/sync")
async def sync_dashboard(
    dashboard_id: int,
    source_env_id: typing.Optional[str] = None,
    _ = Depends(has_permission("plugin:git", "EXECUTE"))
):
    with belief_scope("sync_dashboard"):
        try:
            from src.plugins.git_plugin import GitPlugin
            plugin = GitPlugin()
            return await plugin.execute({
                "operation": "sync",
                "dashboard_id": dashboard_id,
                "source_env_id": source_env_id
            })
        except Exception as e:
            raise HTTPException(status_code=400, detail=str(e))
# [/DEF:sync_dashboard:Function]

# [DEF:get_environments:Function]
# @PURPOSE: List all deployment environments.
# @PRE: Config manager is accessible.
# @POST: Returns a list of DeploymentEnvironmentSchema objects.
# @RETURN: List[DeploymentEnvironmentSchema]
@router.get("/environments", response_model=List[DeploymentEnvironmentSchema])
async def get_environments(
    config_manager=Depends(get_config_manager),
    _ = Depends(has_permission("environments", "READ"))
):
    with belief_scope("get_environments"):
        envs = config_manager.get_environments()
        return [
            DeploymentEnvironmentSchema(
                id=e.id,
                name=e.name,
                superset_url=e.url,
                is_active=True
            ) for e in envs
        ]
# [/DEF:get_environments:Function]

# [DEF:deploy_dashboard:Function]
# @PURPOSE: Deploy dashboard from Git to a target environment.
# @PRE: `dashboard_id` and `deploy_data.environment_id` are valid.
# @POST: Dashboard YAMLs are read from Git and imported into the target Superset.
# @PARAM: dashboard_id (int)
# @PARAM: deploy_data (DeployRequest)
@router.post("/repositories/{dashboard_id}/deploy")
async def deploy_dashboard(
    dashboard_id: int,
    deploy_data: DeployRequest,
    _ = Depends(has_permission("plugin:git", "EXECUTE"))
):
    with belief_scope("deploy_dashboard"):
        try:
            from src.plugins.git_plugin import GitPlugin
            plugin = GitPlugin()
            return await plugin.execute({
                "operation": "deploy", 
                "dashboard_id": dashboard_id,
                "environment_id": deploy_data.environment_id
            })
        except Exception as e:
            raise HTTPException(status_code=400, detail=str(e))
# [/DEF:deploy_dashboard:Function]

# [DEF:get_history:Function]
# @PURPOSE: View commit history for a dashboard's repository.
# @PRE: `dashboard_id` repository exists.
# @POST: Returns a list of recent commits from the repository.
# @PARAM: dashboard_id (int)
# @PARAM: limit (int)
# @RETURN: List[CommitSchema]
@router.get("/repositories/{dashboard_id}/history", response_model=List[CommitSchema])
async def get_history(
    dashboard_id: int,
    limit: int = 50,
    _ = Depends(has_permission("plugin:git", "EXECUTE"))
):
    with belief_scope("get_history"):
        try:
            return git_service.get_commit_history(dashboard_id, limit)
        except Exception as e:
            raise HTTPException(status_code=404, detail=str(e))
# [/DEF:get_history:Function]

# [DEF:get_repository_status:Function]
# @PURPOSE: Get current Git status for a dashboard repository.
# @PRE: `dashboard_id` repository exists.
# @POST: Returns the status of the working directory (staged, unstaged, untracked).
# @PARAM: dashboard_id (int)
# @RETURN: dict
@router.get("/repositories/{dashboard_id}/status")
async def get_repository_status(
    dashboard_id: int,
    _ = Depends(has_permission("plugin:git", "EXECUTE"))
):
    with belief_scope("get_repository_status"):
        try:
            return git_service.get_status(dashboard_id)
        except Exception as e:
            raise HTTPException(status_code=400, detail=str(e))
# [/DEF:get_repository_status:Function]

# [DEF:get_repository_diff:Function]
# @PURPOSE: Get Git diff for a dashboard repository.
# @PRE: `dashboard_id` repository exists.
# @POST: Returns the diff text for the specified file or all changes.
# @PARAM: dashboard_id (int)
# @PARAM: file_path (Optional[str])
# @PARAM: staged (bool)
# @RETURN: str
@router.get("/repositories/{dashboard_id}/diff")
async def get_repository_diff(
    dashboard_id: int,
    file_path: Optional[str] = None,
    staged: bool = False,
    _ = Depends(has_permission("plugin:git", "EXECUTE"))
):
    with belief_scope("get_repository_diff"):
        try:
            diff_text = git_service.get_diff(dashboard_id, file_path, staged)
            return diff_text
        except Exception as e:
            raise HTTPException(status_code=400, detail=str(e))
# [/DEF:get_repository_diff:Function]

# [DEF:generate_commit_message:Function]
# @PURPOSE: Generate a suggested commit message using LLM.
# @PRE: Repository for `dashboard_id` is initialized.
# @POST: Returns a suggested commit message string.
@router.post("/repositories/{dashboard_id}/generate-message")
async def generate_commit_message(
    dashboard_id: int,
    db: Session = Depends(get_db),
    _ = Depends(has_permission("plugin:git", "EXECUTE"))
):
    with belief_scope("generate_commit_message"):
        try:
            # 1. Get Diff
            diff = git_service.get_diff(dashboard_id, staged=True)
            if not diff:
                diff = git_service.get_diff(dashboard_id, staged=False)
            
            if not diff:
                return {"message": "No changes detected"}

            # 2. Get History
            history_objs = git_service.get_commit_history(dashboard_id, limit=5)
            history = [h.message for h in history_objs if hasattr(h, 'message')]

            # 3. Get LLM Client
            from ...services.llm_provider import LLMProviderService
            from ...plugins.llm_analysis.service import LLMClient
            from ...plugins.llm_analysis.models import LLMProviderType
            
            llm_service = LLMProviderService(db)
            providers = llm_service.get_all_providers()
            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
            )

            # 4. Generate Message
            from ...plugins.git.llm_extension import GitLLMExtension
            extension = GitLLMExtension(client)
            message = await extension.suggest_commit_message(diff, history)
            
            return {"message": message}
        except Exception as e:
            logger.error(f"Failed to generate commit message: {e}")
            raise HTTPException(status_code=400, detail=str(e))
# [/DEF:generate_commit_message:Function]

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