"""Git file operations with repo validation.""" import logging import uuid from fastapi import HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from src.models.git_repository import GitRepository from src.models.user import User from src.schemas.git_repository import ( FileContentResponse, FileListResponse, FileUpdateRequest, FileUpdateResponse, ) from src.services.git.repository import ensure_repo_on_disk, get_repo_and_validate from src.utils.git_files import ( commit_file, get_file_content, list_branches, list_tree, ) logger = logging.getLogger(__name__) async def list_files( session: AsyncSession, project_id: uuid.UUID, repo_id: uuid.UUID, branch: str = "main", path: str = "", ) -> FileListResponse: repo = await get_repo_and_validate(session, repo_id, project_id) ensure_repo_on_disk(repo) try: entries = list_tree(repo.path, branch=branch, path=path) return FileListResponse( path=path, branch=branch, entries=[ { "name": e.name, "type": e.type, "path": e.path, "size": e.size, "mode": e.mode, "last_commit": e.last_commit, } for e in entries ], ) except RuntimeError as e: logger.error( "Failed to list files for repo %s (path=%s, branch=%s): %s", repo_id, path, branch, str(e), exc_info=True, ) raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) async def get_file( session: AsyncSession, project_id: uuid.UUID, repo_id: uuid.UUID, branch: str, path: str, ) -> FileContentResponse: repo = await get_repo_and_validate(session, repo_id, project_id) ensure_repo_on_disk(repo) try: file_content = get_file_content(repo.path, branch=branch, path=path) return FileContentResponse( path=file_content.path, branch=file_content.branch, content=file_content.content, size=file_content.size, encoding=file_content.encoding, language=file_content.language, is_binary=file_content.is_binary, last_commit=file_content.last_commit, ) except FileNotFoundError: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="file not found") except RuntimeError as e: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) async def update_file( session: AsyncSession, project_id: uuid.UUID, repo_id: uuid.UUID, data: FileUpdateRequest, user: User, ) -> FileUpdateResponse: repo = await get_repo_and_validate(session, repo_id, project_id) ensure_repo_on_disk(repo) author_name = user.name or "Unknown" author_email = user.email or "unknown@example.com" try: commit_hash = commit_file( repo_path=repo.path, branch=data.branch, path=data.path, content=data.content, commit_message=data.commit_message, author_name=author_name, author_email=author_email, ) return FileUpdateResponse( commit_hash=commit_hash, message=data.commit_message, branch=data.branch, ) except RuntimeError as e: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) async def list_branches_with_validation( session: AsyncSession, project_id: uuid.UUID, repo_id: uuid.UUID, ) -> dict: repo = await get_repo_and_validate(session, repo_id, project_id) ensure_repo_on_disk(repo) try: branches, default_branch = list_branches(repo.path) return { "branches": [ { "name": b.name, "is_default": b.is_default, "last_commit": b.last_commit, } for b in branches ], "default_branch": default_branch, } except RuntimeError as e: logger.error( "Failed to list branches for repo %s: %s", repo_id, str(e), exc_info=True, ) raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))