"""Deploy script execution, health checking, and URL storage."""

from __future__ import annotations

import subprocess
import sys
import time
from pathlib import Path
from typing import List, Optional, Tuple


def run_deploy_script(
    project_dir: Path,
    project_id: str,
    port: int,
    *,
    deploy_script: Optional[str] = None,
    server_app_name: Optional[str] = None,
    timeout: int = 600,
    log=None,
) -> Tuple[bool, str]:
    """Run a deploy script and return ``(success, combined_output)``.

    Resolution order for the script path:
    1. Explicit *deploy_script* argument (absolute or relative to *project_dir*)
    2. ``<project_dir>/deploy.sh``
    3. ``<project_dir>/.autobuilder/deploy.sh``

    The script is invoked as ``bash <script>``. When the script was resolved
    via the explicit path or project root, no extra arguments are passed.
    """
    if log is None:
        log = lambda msg: print(msg, file=sys.stderr)  # noqa: E731

    script_path, is_project_level = _resolve_deploy_script(
        project_dir, deploy_script,
    )
    if script_path is None:
        msg = "No deploy script found — skipping deployment."
        log(msg)
        return True, msg

    app_name = server_app_name or project_id
    cmd = ["bash", str(script_path), app_name, str(project_dir), str(port)]
    log(f"Running deploy: {script_path} {app_name} {project_dir} {port}")

    return _exec_script(cmd, project_dir, timeout, log)


def health_check(
    url: str,
    *,
    attempts: int = 30,
    interval: int = 5,
    log=None,
) -> bool:
    """Repeatedly curl *url* until a 2xx/3xx response is received."""
    if log is None:
        log = lambda msg: print(msg, file=sys.stderr)  # noqa: E731

    log(f"Health-checking {url} (up to {attempts} attempts)...")
    for i in range(1, attempts + 1):
        try:
            result = subprocess.run(
                ["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}",
                 "--max-time", "5", url],
                capture_output=True, text=True, timeout=10,
            )
            code = result.stdout.strip()
        except Exception:
            code = "000"
        if code.startswith("2") or code.startswith("3"):
            log(f"Live! {url} (HTTP {code})")
            return True
        log(f"  Attempt {i}/{attempts}: HTTP {code} — waiting {interval}s...")
        time.sleep(interval)

    log(f"WARNING: {url} not responding after {attempts * interval}s")
    return False


def store_deploy_url(project_dir: Path, url: str) -> None:
    """Write the deploy URL to ``.autobuilder/deploy_url``."""
    target = project_dir / ".autobuilder" / "deploy_url"
    target.parent.mkdir(parents=True, exist_ok=True)
    target.write_text(url.strip() + "\n", encoding="utf-8")


# ------------------------------------------------------------------
# Internal helpers
# ------------------------------------------------------------------

def _resolve_deploy_script(
    project_dir: Path,
    explicit: Optional[str],
) -> Tuple[Optional[Path], bool]:
    """Return ``(script_path, is_project_level)`` or ``(None, False)``.

    Resolution order:
    1. Explicit path (absolute or relative to *project_dir*)
    2. ``<project_dir>/deploy.sh``
    3. ``<project_dir>/.autobuilder/deploy.sh``
    """
    if explicit:
        p = Path(explicit)
        if not p.is_absolute():
            p = project_dir / p
        if p.is_file():
            return p, True
        return None, False

    project_level = project_dir / "deploy.sh"
    if project_level.is_file():
        return project_level, True

    autobuilder_level = project_dir / ".autobuilder" / "deploy.sh"
    if autobuilder_level.is_file():
        return autobuilder_level, True

    return None, False


def _exec_script(
    cmd: List[str],
    cwd: Path,
    timeout: int,
    log,
) -> Tuple[bool, str]:
    """Execute a shell command, log output, return ``(ok, combined)``."""
    try:
        result = subprocess.run(
            cmd, cwd=str(cwd), capture_output=True, text=True, timeout=timeout,
        )
    except subprocess.TimeoutExpired:
        msg = f"Deploy script timed out ({timeout}s)."
        log(msg)
        return False, msg
    except Exception as e:
        msg = f"Deploy script error: {e}"
        log(msg)
        return False, msg

    output_parts: List[str] = []
    if result.stdout.strip():
        for line in result.stdout.strip().splitlines():
            log(f"  [deploy] {line}")
        output_parts.append(result.stdout.strip())
    if result.stderr.strip():
        for line in result.stderr.strip().splitlines():
            log(f"  [deploy:err] {line}")
        output_parts.append(f"STDERR:\n{result.stderr.strip()}")
    combined = "\n".join(output_parts)

    if result.returncode == 0:
        log("Deploy script succeeded.")
        return True, combined
    log(f"Deploy script failed (exit code {result.returncode}).")
    return False, combined
