"""Bernard Temporal activities — Gitea PR review & Temporal workflow monitoring."""
from __future__ import annotations

import json
import os
from datetime import datetime, timezone
from typing import Any

import httpx
import psycopg
from temporalio import activity


# ---------------------------------------------------------------------------
# Gitea helpers
# ---------------------------------------------------------------------------

def _gitea_headers() -> dict:
    return {
        "Authorization": f"token {os.environ['GITEA_TOKEN']}",
        "Accept": "application/json",
    }


def _gitea_base() -> str:
    return os.environ.get("GITEA_BASE_URL", "http://gitea.gitea.svc.cluster.local:3000")


# ---------------------------------------------------------------------------
# PR Review activities
# ---------------------------------------------------------------------------

@activity.defn
async def fetch_open_prs(repo_full_name: str) -> list[dict[str, Any]]:
    """Return list of open PRs from Gitea for the given repo (owner/repo)."""
    url = f"{_gitea_base()}/api/v1/repos/{repo_full_name}/pulls"
    async with httpx.AsyncClient(timeout=30) as client:
        resp = await client.get(url, headers=_gitea_headers(), params={"state": "open", "limit": 20})
        resp.raise_for_status()
        return resp.json()


@activity.defn
async def fetch_pr_diff(payload: dict[str, Any]) -> dict[str, Any]:
    """Fetch the unified diff for a specific PR."""
    repo = payload["repo"]
    pr_number = payload["pr_number"]
    url = f"{_gitea_base()}/api/v1/repos/{repo}/pulls/{pr_number}.diff"
    async with httpx.AsyncClient(timeout=30) as client:
        resp = await client.get(url, headers=_gitea_headers())
        resp.raise_for_status()
        pr_url = f"{_gitea_base()}/api/v1/repos/{repo}/pulls/{pr_number}"
        pr_resp = await client.get(pr_url, headers=_gitea_headers())
        pr_resp.raise_for_status()
        pr_data = pr_resp.json()
        return {
            "repo": repo,
            "pr_number": pr_number,
            "title": pr_data.get("title", ""),
            "author": pr_data.get("user", {}).get("login", ""),
            "diff": resp.text,
        }


@activity.defn
async def review_pr_with_llm(payload: dict[str, Any]) -> dict[str, Any]:
    """Send diff to LiteLLM/Ollama and get a structured code review."""
    from bernard.llm import review_code_diff
    review = await review_code_diff(
        title=payload["title"],
        author=payload["author"],
        diff=payload["diff"],
    )
    return {
        "repo": payload["repo"],
        "pr_number": payload["pr_number"],
        "title": payload["title"],
        "author": payload["author"],
        "review": review,
    }


@activity.defn
async def post_pr_comment(payload: dict[str, Any]) -> None:
    """Post the AI review as a comment on the Gitea PR."""
    repo = payload["repo"]
    pr_number = payload["pr_number"]
    review = payload["review"]
    body = f"## 🤖 Bernard AI Code Review\n\n{review}\n\n---\n*Reviewed by Bernard — Agentic OS monitoring agent*"
    url = f"{_gitea_base()}/api/v1/repos/{repo}/issues/{pr_number}/comments"
    async with httpx.AsyncClient(timeout=30) as client:
        resp = await client.post(url, headers=_gitea_headers(), json={"body": body})
        resp.raise_for_status()


@activity.defn
async def get_reviewed_prs() -> set[str]:
    """Fetch set of already-reviewed PR keys from the bernard DB."""
    dsn = os.environ["BERNARD_DB_DSN"]
    conn = await psycopg.AsyncConnection.connect(dsn)
    try:
        async with conn.cursor() as cur:
            await cur.execute("""
                CREATE TABLE IF NOT EXISTS bernard_reviewed_prs (
                    id SERIAL PRIMARY KEY,
                    pr_key TEXT UNIQUE NOT NULL,
                    reviewed_at TIMESTAMPTZ DEFAULT NOW()
                )
            """)
            await conn.commit()
            await cur.execute("SELECT pr_key FROM bernard_reviewed_prs")
            rows = await cur.fetchall()
            return {row[0] for row in rows}
    finally:
        await conn.close()


@activity.defn
async def mark_pr_reviewed(pr_key: str) -> None:
    """Record that a PR has been reviewed to avoid duplicate comments."""
    dsn = os.environ["BERNARD_DB_DSN"]
    conn = await psycopg.AsyncConnection.connect(dsn)
    try:
        async with conn.cursor() as cur:
            await cur.execute(
                "INSERT INTO bernard_reviewed_prs (pr_key) VALUES (%s) ON CONFLICT DO NOTHING",
                (pr_key,),
            )
        await conn.commit()
    finally:
        await conn.close()


# ---------------------------------------------------------------------------
# Temporal monitoring activities
# ---------------------------------------------------------------------------

@activity.defn
async def fetch_failed_workflows() -> list[dict[str, Any]]:
    """Query Temporal for workflows that failed in the last check window."""
    from temporalio.client import Client
    address = os.environ.get("TEMPORAL_ADDRESS", "temporal-frontend.ai-core.svc.cluster.local:7233")
    namespace = os.environ.get("TEMPORAL_NAMESPACE", "default")
    client = await Client.connect(address, namespace=namespace)

    failed = []
    async for wf in client.list_workflows('ExecutionStatus = "Failed"'):
        failed.append({
            "workflow_id": wf.id,
            "run_id": wf.run_id,
            "workflow_type": wf.workflow_type,
            "start_time": wf.start_time.isoformat() if wf.start_time else None,
            "close_time": wf.close_time.isoformat() if wf.close_time else None,
        })
    return failed


@activity.defn
async def fetch_workflow_failure_details(payload: dict[str, Any]) -> dict[str, Any]:
    """Get the detailed error and history for a failed workflow."""
    from temporalio.client import Client
    address = os.environ.get("TEMPORAL_ADDRESS", "temporal-frontend.ai-core.svc.cluster.local:7233")
    namespace = os.environ.get("TEMPORAL_NAMESPACE", "default")
    client = await Client.connect(address, namespace=namespace)

    handle = client.get_workflow_handle(payload["workflow_id"], run_id=payload["run_id"])
    try:
        await handle.result()
        error_message = "No error (workflow may have recovered)"
    except Exception as e:
        error_message = str(e)

    return {
        "workflow_id": payload["workflow_id"],
        "run_id": payload["run_id"],
        "workflow_type": payload["workflow_type"],
        "error": error_message,
    }


@activity.defn
async def analyze_failure_with_llm(payload: dict[str, Any]) -> dict[str, Any]:
    """Use LiteLLM to analyze the failure and suggest a fix."""
    from bernard.llm import analyze_workflow_failure
    analysis = await analyze_workflow_failure(
        workflow_id=payload["workflow_id"],
        workflow_type=payload["workflow_type"],
        error=payload["error"],
    )
    return {**payload, "analysis": analysis}


# ---------------------------------------------------------------------------
# Shared: persist alert and dashboard update
# ---------------------------------------------------------------------------

@activity.defn
async def persist_alert(payload: dict[str, Any]) -> str:
    """Persist an alert to the bernard DB and return the alert ID."""
    dsn = os.environ["BERNARD_DB_DSN"]
    conn = await psycopg.AsyncConnection.connect(dsn)
    try:
        async with conn.cursor() as cur:
            await cur.execute("""
                CREATE TABLE IF NOT EXISTS bernard_alerts (
                    id SERIAL PRIMARY KEY,
                    alert_type TEXT NOT NULL,
                    title TEXT NOT NULL,
                    body TEXT NOT NULL,
                    metadata JSONB DEFAULT '{}',
                    created_at TIMESTAMPTZ DEFAULT NOW()
                )
            """)
            await conn.commit()
            await cur.execute(
                """INSERT INTO bernard_alerts (alert_type, title, body, metadata)
                   VALUES (%s, %s, %s, %s) RETURNING id""",
                (
                    payload["alert_type"],
                    payload["title"],
                    payload["body"],
                    json.dumps(payload.get("metadata", {})),
                ),
            )
            row = await cur.fetchone()
            await conn.commit()
            return str(row[0])
    finally:
        await conn.close()
