"""Pull a project's GitHub preview branch without invoking git.

The script uses the P2HS GitHub App installation token and GitHub's tree/blob
API. It writes tracked files into ``--destination`` and never deletes existing
local files. Use a fresh directory when an exact clean mirror is required.

Usage:

    python -m scripts.pull_project_preview_with_app \
      --project-id <customer-project-uuid> \
      --destination E:\\path\\to\\customer-checkout
"""
from __future__ import annotations

import argparse
import asyncio
from pathlib import Path, PurePosixPath
from uuid import UUID

from src.core.orm_import import import_all_orm_models

import_all_orm_models()

from src.config import get_settings  # noqa: E402
from src.core.database import async_session_factory  # noqa: E402
from src.domain.projects.repository import ProjectRepository  # noqa: E402


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--project-id", required=True, type=UUID)
    parser.add_argument("--destination", required=True, type=Path)
    return parser.parse_args()


async def run(args: argparse.Namespace) -> int:
    async with async_session_factory() as db:
        project = await ProjectRepository(db).get_with_repo(args.project_id)
    if project is None or project.repo is None:
        raise RuntimeError(f"Project {args.project_id} has no linked GitHub repository")

    settings = get_settings()
    repo = project.repo.repo_full_name
    branch = project.repo.preview_branch or settings.hosting_preview_branch or "preview"
    if branch != "preview":
        raise RuntimeError(
            f"Refusing to pull {branch!r}; this script is restricted to the preview branch"
        )

    from src.integrations.github_client import GitHubAppClient
    from src.integrations.github_installation import resolve_installation_id_for_workspace

    installation_id = await resolve_installation_id_for_workspace(
        async_session_factory,
        project.workspace_id,
    )
    client = GitHubAppClient(installation_id=installation_id)
    paths = sorted(await client.list_tree_paths(repo, branch))
    destination = args.destination.resolve()
    destination.mkdir(parents=True, exist_ok=True)

    print(f"Pulling {len(paths)} tracked files from {repo}:{branch}")
    for index, relative in enumerate(paths, start=1):
        safe = PurePosixPath(relative)
        if safe.is_absolute() or ".." in safe.parts:
            raise RuntimeError(f"unsafe path returned by GitHub: {relative}")
        target = destination.joinpath(*safe.parts)
        target.parent.mkdir(parents=True, exist_ok=True)
        target.write_bytes(await client.get_file_contents(repo, relative, branch))
        if index % 50 == 0 or index == len(paths):
            print(f"  {index}/{len(paths)}")

    print(f"Pulled {repo}:{branch} into {destination}")
    return 0


def main() -> None:
    try:
        raise SystemExit(asyncio.run(run(parse_args())))
    except (OSError, RuntimeError, ValueError) as exc:
        raise SystemExit(f"ERROR: {exc}") from exc


if __name__ == "__main__":
    main()
