"""GitHub pull_request.closed (merged) handler.

On a pull request being merged into a project's repository, this handler
reads the project's deploy_config and — if enabled — triggers a GitHub Actions
workflow_dispatch event via the ``gh`` CLI.

The target workflow file, ref, and optional inputs are all configurable
per-project via the ``deploy_config`` column on the projects table.
"""

from __future__ import annotations

import asyncio
import json
import logging
import re

from peon_mcp.db import get_pool
from peon_mcp.github_webhooks.dispatcher import register_handler

logger = logging.getLogger(__name__)

# Matches the owner/repo portion of a GitHub URL.
_REPO_RE = re.compile(r"github\.com[/:]([^/]+/[^/.]+?)(?:\.git)?$")


def _extract_repo_name(github_url: str) -> str:
    """Return 'owner/repo' from a full GitHub URL, or empty string."""
    m = _REPO_RE.search(github_url)
    return m.group(1) if m else ""


@register_handler("pull_request")
async def handle_pr_merge(delivery_id: str, payload: dict) -> None:
    """Handle pull_request.closed events where merged=True.

    Finds all projects whose github_url matches the PR's base repository,
    loads their deploy_config, and triggers a workflow_dispatch if the config
    is enabled and has a workflow_id set.
    """
    action = payload.get("action", "")
    if action != "closed":
        return

    pr = payload.get("pull_request", {})
    if not pr.get("merged"):
        return

    base_repo_full = pr.get("base", {}).get("repo", {}).get("full_name", "")
    if not base_repo_full:
        logger.debug("pr_merge_handler: delivery=%s missing base.repo.full_name", delivery_id)
        return

    logger.info(
        "pr_merge_handler: delivery=%s merged PR in repo=%s",
        delivery_id,
        base_repo_full,
    )

    pool = get_pool()
    async with pool.acquire() as conn:
        # Find projects whose github_url contains this repo path.
        rows = await conn.fetch(
            "SELECT id, github_url, deploy_config FROM projects WHERE github_url != ''",
        )

    for row in rows:
        project_id: str = row["id"]
        project_repo = _extract_repo_name(row["github_url"] or "")
        if project_repo.lower() != base_repo_full.lower():
            continue

        raw = row["deploy_config"] or "{}"
        try:
            cfg = json.loads(raw)
        except (json.JSONDecodeError, TypeError):
            cfg = {}

        if not cfg.get("enabled"):
            logger.debug(
                "pr_merge_handler: project=%s deploy disabled, skipping",
                project_id,
            )
            continue

        workflow_id: str = cfg.get("workflow_id", "")
        if not workflow_id:
            logger.warning(
                "pr_merge_handler: project=%s deploy enabled but no workflow_id configured",
                project_id,
            )
            continue

        # Use the configured target repo, defaulting to the PR's own repo.
        target_repo: str = cfg.get("github_repo", "") or base_repo_full
        ref: str = cfg.get("ref", "main") or "main"
        inputs: dict = cfg.get("inputs") or {}

        await _trigger_workflow_dispatch(
            delivery_id=delivery_id,
            project_id=project_id,
            github_repo=target_repo,
            workflow_id=workflow_id,
            ref=ref,
            inputs=inputs,
        )


async def _trigger_workflow_dispatch(
    *,
    delivery_id: str,
    project_id: str,
    github_repo: str,
    workflow_id: str,
    ref: str,
    inputs: dict,
) -> None:
    """Trigger a GitHub Actions workflow_dispatch via the ``gh`` CLI.

    Uses ``gh api --method POST`` to call the GitHub REST API endpoint::

        POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches

    The ``gh`` CLI handles authentication transparently via the GitHub App
    installation token injected as ``GH_TOKEN``.
    """
    body: dict = {"ref": ref}
    if inputs:
        body["inputs"] = inputs

    cmd = [
        "gh", "api",
        f"/repos/{github_repo}/actions/workflows/{workflow_id}/dispatches",
        "--method", "POST",
        "--input", "-",
    ]

    logger.info(
        "pr_merge_handler: delivery=%s project=%s triggering workflow=%s repo=%s ref=%s",
        delivery_id,
        project_id,
        workflow_id,
        github_repo,
        ref,
    )

    try:
        proc = await asyncio.create_subprocess_exec(
            *cmd,
            stdin=asyncio.subprocess.PIPE,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE,
        )
        body_bytes = json.dumps(body).encode()
        stdout, stderr = await asyncio.wait_for(
            proc.communicate(input=body_bytes),
            timeout=30,
        )

        if proc.returncode == 0:
            logger.info(
                "pr_merge_handler: delivery=%s project=%s workflow dispatch successful",
                delivery_id,
                project_id,
            )
        else:
            logger.error(
                "pr_merge_handler: delivery=%s project=%s workflow dispatch failed "
                "(exit=%d): %s",
                delivery_id,
                project_id,
                proc.returncode,
                stderr.decode(errors="replace"),
            )

    except asyncio.TimeoutError:
        logger.error(
            "pr_merge_handler: delivery=%s project=%s workflow dispatch timed out",
            delivery_id,
            project_id,
        )
    except Exception:
        logger.exception(
            "pr_merge_handler: delivery=%s project=%s unexpected error in workflow dispatch",
            delivery_id,
            project_id,
        )
