"""Push selected customer files to a project's preview branch via GitHub App.

This is the P2HS-BE version of the preview patch flow. It does not invoke git
and does not use a personal GitHub token. ``GitHubAppClient`` mints the
short-lived installation token from the configured App private key.

Usage from the P2HS-BE root:

    python -m scripts.push_project_preview_with_app \
      --project-id <customer-project-uuid> \
      --source-root E:\\path\\to\\customer-checkout

If ``--source-root`` is omitted, the configured P2HS repo workspace checkout
for the project is used.
"""
from __future__ import annotations

import argparse
import asyncio
from pathlib import Path
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
from src.domain.projects.paths import get_project_repo_path  # noqa: E402

FILES = (
    "pages/admin/index.vue",
    "pages/admin/login.vue",
    "pages/admin/forgot-password.vue",
    "pages/admin/reset-password.vue",
)


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--project-id", required=True, type=UUID)
    parser.add_argument("--source-root", type=Path)
    parser.add_argument("--message", default="Fix admin sign-in to use magic links")
    return parser.parse_args()


async def load_project(project_id: UUID):
    async with async_session_factory() as db:
        project = await ProjectRepository(db).get_with_repo(project_id)
        if project is None or project.repo is None:
            raise RuntimeError(f"Project {project_id} has no linked GitHub repository")
        return project


async def run(args: argparse.Namespace) -> int:
    project = await load_project(args.project_id)
    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 update {branch!r}; this script is restricted to the preview branch"
        )

    source_root = (
        args.source_root.resolve()
        if args.source_root
        else Path(get_project_repo_path(settings.repo_workspace, project.workspace_id, project.id))
    )
    files: dict[str, bytes] = {}
    for relative in FILES:
        path = source_root / Path(*relative.split("/"))
        if not path.is_file():
            raise FileNotFoundError(f"source file not found: {path}")
        files[relative] = path.read_bytes()

    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)

    print(f"Pushing {len(files)} admin auth files to {repo}:{branch}")
    commit_sha = await client.push_files(repo, branch, files, args.message)
    print(f"Pushed commit {commit_sha} to {repo}:{branch}")
    return 0


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


if __name__ == "__main__":
    main()
