#!/usr/bin/env python3
"""post-push hook: CI monitoring + Vercel deployment monitoring.

Use this template when your project deploys to Vercel. This hook waits
for CI to pass, then polls Vercel for the deployment to complete and
health-checks the preview URL.

Copy (or symlink) this file to your project's
``.autobuilder/hooks/post-push.py``.

Prerequisites:
    - ``vercel`` CLI installed and linked to your project
      (run ``vercel link`` in the project directory once)
    - ``gh`` CLI for CI monitoring (optional)

Environment variables (set automatically by the autobuilder daemon):
    AUTOBUILDER_PROJECT_DIR   — absolute path to the project
    AUTOBUILDER_PROJECT_ID    — project identifier
    AUTOBUILDER_PUSH_BRANCH   — branch that was just pushed
    AUTOBUILDER_EXECUTOR      — "cursor" or "codex"
    AUTOBUILDER_MODEL         — model name (may be empty)

Exit codes:
    0 — CI passed and Vercel deployment succeeded
    1 — CI failure or Vercel deployment failure

Optional deploy.json keys::

    {
        "max_ci_fix_attempts": 3,
        "ci_timeout": 600,
        "ci_poll_interval": 15,
        "vercel_timeout": 600,
        "vercel_poll_interval": 10
    }
"""

from __future__ import annotations

import json
import os
import subprocess
import sys
from pathlib import Path
from typing import Any, Dict

from autobuilder_hook_helpers.ci import wait_for_ci
from autobuilder_hook_helpers.deploy import health_check, store_deploy_url
from autobuilder_hook_helpers.fix_agent import build_ci_fix_prompt, run_fix_agent
from autobuilder_hook_helpers.github import (
    get_ci_failure_logs,
    get_github_remote,
    get_head_sha,
    has_github_workflows,
)
from autobuilder_hook_helpers.vercel import wait_for_vercel_deploy

DEFAULTS: Dict[str, Any] = {
    "max_ci_fix_attempts": 3,
    "ci_timeout": 600,
    "ci_poll_interval": 15,
    "vercel_timeout": 600,
    "vercel_poll_interval": 10,
}


def _load_config(project_dir: Path) -> Dict[str, Any]:
    cfg_file = project_dir / ".autobuilder" / "deploy.json"
    cfg = dict(DEFAULTS)
    if cfg_file.is_file():
        try:
            user_cfg = json.loads(cfg_file.read_text(encoding="utf-8"))
            cfg.update(user_cfg)
        except Exception as exc:
            _log(f"WARNING: could not parse {cfg_file}: {exc}")
    return cfg


def _log(msg: str) -> None:
    print(msg, file=sys.stderr, flush=True)


def _push_local_commits(project_dir: Path, branch: str) -> bool:
    """Push any local commits (from fix agents) to origin."""
    try:
        result = subprocess.run(
            ["git", "rev-list", "--count", f"origin/{branch}..HEAD"],
            cwd=str(project_dir), capture_output=True, text=True, timeout=15,
        )
        if result.returncode != 0 or int(result.stdout.strip()) == 0:
            return True
    except Exception:
        return True

    _log("Pushing fix-agent commits...")
    result = subprocess.run(
        ["git", "push", "origin", branch],
        cwd=str(project_dir), capture_output=True, text=True, timeout=120,
    )
    if result.returncode == 0:
        _log("Push succeeded.")
        return True
    _log(f"Push failed: {result.stderr.strip()}")
    return False


def _run_ci_pipeline(
    project_dir: Path,
    owner: str,
    repo: str,
    branch: str,
    cfg: Dict[str, Any],
    executor: str,
    model: str,
) -> bool:
    """Wait for CI, fix on failure, return True if CI passes."""
    max_attempts = cfg["max_ci_fix_attempts"]

    for attempt in range(1, max_attempts + 1):
        sha = get_head_sha(project_dir, branch=branch)
        if not sha:
            _log("Could not determine HEAD sha — skipping CI wait.")
            return True

        ci_ok, run_id = wait_for_ci(
            project_dir, owner, repo, sha,
            branch=branch,
            timeout=cfg["ci_timeout"],
            poll_interval=cfg["ci_poll_interval"],
            log=_log,
        )
        if ci_ok:
            return True

        if attempt >= max_attempts:
            _log(f"CI still failing after {max_attempts} fix attempts.")
            return False

        ci_logs = get_ci_failure_logs(project_dir, owner, repo, run_id) if run_id else ""
        if not ci_logs:
            _log("No CI logs available — cannot auto-fix.")
            return False

        _log(f"CI failed (attempt {attempt}/{max_attempts}) — spawning fix agent...")
        prompt = build_ci_fix_prompt(ci_logs)
        run_fix_agent(
            project_dir, prompt,
            executor=executor, model=model, log=_log,
        )

        if not _push_local_commits(project_dir, branch):
            _log("Could not push fix-agent commits.")
            return False

    return False


def main() -> int:
    project_dir = Path(os.environ.get("AUTOBUILDER_PROJECT_DIR", ".")).resolve()
    project_id = os.environ.get("AUTOBUILDER_PROJECT_ID", project_dir.name)
    branch = os.environ.get("AUTOBUILDER_PUSH_BRANCH", "main")
    executor = os.environ.get("AUTOBUILDER_EXECUTOR", "cursor")
    model = os.environ.get("AUTOBUILDER_MODEL", "")

    cfg = _load_config(project_dir)

    # --- CI ---
    remote = get_github_remote(project_dir)
    if remote:
        owner, repo = remote
        _log(f"GitHub remote: {owner}/{repo}")

        has_ci = has_github_workflows(project_dir, owner, repo)
        if has_ci:
            ci_ok = _run_ci_pipeline(
                project_dir, owner, repo, branch, cfg, executor, model,
            )
            if not ci_ok:
                print(
                    f"CI pipeline failed for {owner}/{repo} after "
                    f"{cfg['max_ci_fix_attempts']} fix attempts.",
                    flush=True,
                )
                return 1
    else:
        _log("No GitHub remote — skipping CI.")

    # --- Vercel deploy ---
    _log("Waiting for Vercel deployment...")
    deploy_ok, preview_url = wait_for_vercel_deploy(
        project_dir,
        timeout=cfg.get("vercel_timeout", 600),
        poll_interval=cfg.get("vercel_poll_interval", 10),
        log=_log,
    )

    if not deploy_ok:
        print(f"Vercel deployment failed for {project_id}.", flush=True)
        return 1

    if preview_url:
        if not health_check(preview_url, log=_log):
            _log(f"WARNING: {preview_url} failed health check.")
            print(f"Vercel health check failed: {preview_url}", flush=True)
            return 1
        store_deploy_url(project_dir, preview_url)
        _log(f"Vercel deployment live: {preview_url}")

    return 0


if __name__ == "__main__":
    raise SystemExit(main())
