"""Worker task — sync platform-managed files to project/template repos.

Pushes .cursor/instructions.md and p2hs/ contract docs
when a repo's ``platform_files_version`` is NULL or behind the current version.
``p2hs.platform.json`` is a Class C control artifact and is not Git-exported.
User code is never touched — only the managed file paths are written.

For a template, both branches get the push: ``starter_branch`` (what a new
project actually clones when created from this template — the functionally
required one, and the one that gates ``platform_files_version``) and
``main_branch`` (the live public demo, refreshed for consistency but never
blocking on failure).

Triggered by:
  - POST /api/admin/platform-files/sync  (manual / bulk)
  - Optionally: pre-codegen check in run_codegen (future)
"""
from __future__ import annotations

from typing import Optional
from uuid import UUID

import structlog

from src.domain.projects.platform_files_sync import (
    MANAGED_FILE_PATHS,
    PLATFORM_FILES_VERSION,
    get_managed_files,
)

logger = structlog.get_logger()

_COMMIT_MSG = f"chore(platform): update platform files to v{PLATFORM_FILES_VERSION}"


async def sync_platform_files(
    ctx: dict,
    *,
    project_id: Optional[str] = None,
    template_id: Optional[str] = None,
) -> dict:
    """Sync platform-managed files to one or all stale repos.

    - project_id given  → sync that project only
    - template_id given → sync that template only
    - neither given     → bulk: sync up to 50 stale projects + 50 stale templates
    """
    from src.core.database import async_session_factory
    from src.domain.projects.repository import ProjectRepository
    from src.domain.templates.repository import TemplateRepository
    from src.integrations.github_client import GitHubAppClient
    from src.config import get_settings

    settings = get_settings()
    db_factory = ctx.get("db_factory") or async_session_factory

    projects_synced = 0
    templates_synced = 0
    projects_skipped = 0
    templates_skipped = 0

    async with db_factory() as db:
        project_repo = ProjectRepository(db)
        template_repo = TemplateRepository(db)

        # ── Projects ──────────────────────────────────────────────────────────
        if project_id:
            pairs = []
            row = await project_repo.get_with_repo(UUID(project_id))
            if row and hasattr(row, "repo") and row.repo:
                pairs = [(row, row.repo)]
        else:
            pairs = await project_repo.get_stale_platform_files(PLATFORM_FILES_VERSION)

        for project, repo in pairs:
            framework = project.type
            if framework not in MANAGED_FILE_PATHS:
                projects_skipped += 1
                continue

            files = get_managed_files(framework, settings)
            if not files:
                projects_skipped += 1
                continue

            github = GitHubAppClient()
            try:
                await github.create_commit(
                    repo.repo_full_name,
                    repo.default_branch,
                    files,
                    _COMMIT_MSG,
                )
                await project_repo.set_platform_files_version(project.id, PLATFORM_FILES_VERSION)
                await db.commit()
                projects_synced += 1
                logger.info(
                    "platform_files_synced",
                    entity="project",
                    project_id=str(project.id),
                    repo=repo.repo_full_name,
                    version=PLATFORM_FILES_VERSION,
                )
            except Exception as exc:
                await db.rollback()
                projects_skipped += 1
                logger.warning(
                    "platform_files_sync_failed",
                    entity="project",
                    project_id=str(project.id),
                    repo=repo.repo_full_name,
                    error=str(exc),
                )

        # ── Templates ─────────────────────────────────────────────────────────
        if template_id:
            templates = []
            t = await template_repo.get_by_id(UUID(template_id))
            if t:
                templates = [t]
        else:
            templates = await template_repo.get_stale_platform_files(PLATFORM_FILES_VERSION)

        for template in templates:
            framework = template.framework
            if framework not in MANAGED_FILE_PATHS:
                templates_skipped += 1
                continue

            files = get_managed_files(framework, settings)
            if not files:
                templates_skipped += 1
                continue

            installation_id = template.github_installation_id or None
            github = GitHubAppClient(installation_id=installation_id)

            # starter_branch is what a new project actually clones when created from this
            # template — that's the functionally required push. main_branch is only the
            # live public demo; refresh it too for consistency, but never let a demo-branch
            # failure block marking the template synced.
            starter_ok = False
            try:
                await github.create_commit(
                    template.github_repo,
                    template.starter_branch,
                    files,
                    _COMMIT_MSG,
                )
                starter_ok = True
                logger.info(
                    "platform_files_synced",
                    entity="template",
                    branch="starter",
                    template_id=str(template.id),
                    repo=template.github_repo,
                    version=PLATFORM_FILES_VERSION,
                )
            except Exception as exc:
                logger.warning(
                    "platform_files_sync_failed",
                    entity="template",
                    branch="starter",
                    template_id=str(template.id),
                    repo=template.github_repo,
                    error=str(exc),
                )

            try:
                await github.create_commit(
                    template.github_repo,
                    template.main_branch,
                    files,
                    _COMMIT_MSG,
                )
                logger.info(
                    "platform_files_synced",
                    entity="template",
                    branch="main",
                    template_id=str(template.id),
                    repo=template.github_repo,
                    version=PLATFORM_FILES_VERSION,
                )
            except Exception as exc:
                logger.warning(
                    "platform_files_sync_failed",
                    entity="template",
                    branch="main",
                    template_id=str(template.id),
                    repo=template.github_repo,
                    error=str(exc),
                )

            if starter_ok:
                try:
                    await template_repo.set_platform_files_version(template.id, PLATFORM_FILES_VERSION)
                    await db.commit()
                    templates_synced += 1
                except Exception as exc:
                    await db.rollback()
                    templates_skipped += 1
                    logger.warning(
                        "platform_files_sync_failed",
                        entity="template",
                        branch="version_bookkeeping",
                        template_id=str(template.id),
                        repo=template.github_repo,
                        error=str(exc),
                    )
            else:
                await db.rollback()
                templates_skipped += 1

    logger.info(
        "platform_files_sync_complete",
        projects_synced=projects_synced,
        projects_skipped=projects_skipped,
        templates_synced=templates_synced,
        templates_skipped=templates_skipped,
        version=PLATFORM_FILES_VERSION,
    )
    return {
        "projects_synced": projects_synced,
        "projects_skipped": projects_skipped,
        "templates_synced": templates_synced,
        "templates_skipped": templates_skipped,
    }
