"""Template platform-files sync must push to starter_branch (what new projects clone),
not only main_branch (the live public demo)."""

from __future__ import annotations

from contextlib import asynccontextmanager
from types import SimpleNamespace
from unittest.mock import AsyncMock, patch
from uuid import uuid4

import pytest

from src.workers.tasks.sync_platform_files import sync_platform_files


def _template(**overrides) -> SimpleNamespace:
    base = dict(
        id=uuid4(),
        framework="nuxt_app",
        github_repo="p2hs/tpl-demo",
        github_installation_id=0,
        starter_branch="starter",
        main_branch="main",
    )
    base.update(overrides)
    return SimpleNamespace(**base)


def _fake_db_factory(db):
    @asynccontextmanager
    async def factory():
        yield db

    return factory


def _fake_db():
    db = SimpleNamespace()
    db.commit = AsyncMock()
    db.rollback = AsyncMock()
    return db


async def _run_with(template, *, starter_error=None, main_error=None):
    db = _fake_db()

    project_repo = SimpleNamespace(get_stale_platform_files=AsyncMock(return_value=[]))
    template_repo = SimpleNamespace(
        get_stale_platform_files=AsyncMock(return_value=[template]),
        set_platform_files_version=AsyncMock(),
    )

    async def fake_create_commit(repo, branch, files, message):
        if branch == template.starter_branch and starter_error:
            raise starter_error
        if branch == template.main_branch and main_error:
            raise main_error
        return "sha"

    github_client = SimpleNamespace(create_commit=AsyncMock(side_effect=fake_create_commit))

    with (
        patch(
            "src.domain.projects.repository.ProjectRepository",
            return_value=project_repo,
        ),
        patch(
            "src.domain.templates.repository.TemplateRepository",
            return_value=template_repo,
        ),
        patch(
            "src.integrations.github_client.GitHubAppClient",
            return_value=github_client,
        ),
        patch(
            "src.workers.tasks.sync_platform_files.get_managed_files",
            return_value={"p2hs.platform.json": "{}"},
        ),
    ):
        result = await sync_platform_files({"db_factory": _fake_db_factory(db)})

    return result, template_repo, github_client


@pytest.mark.asyncio
async def test_template_sync_pushes_both_branches():
    template = _template()
    result, template_repo, github_client = await _run_with(template)

    branches_called = {c.args[1] for c in github_client.create_commit.await_args_list}
    assert branches_called == {"starter", "main"}
    template_repo.set_platform_files_version.assert_awaited_once()
    assert result["templates_synced"] == 1
    assert result["templates_skipped"] == 0


@pytest.mark.asyncio
async def test_starter_branch_failure_does_not_mark_template_synced():
    template = _template()
    result, template_repo, _ = await _run_with(template, starter_error=RuntimeError("boom"))

    template_repo.set_platform_files_version.assert_not_awaited()
    assert result["templates_synced"] == 0
    assert result["templates_skipped"] == 1


@pytest.mark.asyncio
async def test_main_branch_failure_still_marks_template_synced():
    template = _template()
    result, template_repo, github_client = await _run_with(template, main_error=RuntimeError("boom"))

    # starter push still attempted and succeeded
    branches_called = {c.args[1] for c in github_client.create_commit.await_args_list}
    assert "starter" in branches_called
    template_repo.set_platform_files_version.assert_awaited_once()
    assert result["templates_synced"] == 1
    assert result["templates_skipped"] == 0
