"""Handle pull_request.closed events — auto-create doc-update PRs on merge."""

from __future__ import annotations

import logging

from ...agent.analyzer import extract_analysis_data
from ...parser.models import ParseOptions
from ...parser.parse import parse_spec
from ...parser.writer import (
    RealizationInsertion,
    insert_realization_comments,
    update_frontmatter_field,
)
from ..spec_utils import filter_spec_files, is_spec_file, load_repo_config, load_repo_specs

logger = logging.getLogger(__name__)

BOT_MARKER = "<!-- specwright-bot -->"


async def on_pull_request_merged(client, payload: dict) -> None:
    """Handle a merged PR — create doc-update PRs if needed.

    Args:
        client: GitHubClient instance.
        payload: The webhook payload.
    """
    pr = payload["pull_request"]
    pr_number = pr["number"]

    # Only act on merged PRs
    if not pr.get("merged"):
        return

    # Skip our own doc-update PRs to avoid infinite loops
    if pr["head"]["ref"].startswith("specwright/"):
        return

    owner = payload["repository"]["owner"]["login"]
    repo = payload["repository"]["name"]
    base_ref = pr["base"]["ref"]

    # Check repo config — respect agents.doc_updates setting
    config = await load_repo_config(client, owner, repo, ref=base_ref)
    doc_paths = config.specs.doc_paths

    if not config.agents.doc_updates:
        logger.info("Doc updates disabled via config for PR #%d", pr_number)
        return

    # Auto-advance review_status on approved PR merge
    try:
        pr_files = await client.list_pull_files(owner, repo, pr_number)
        merged_spec_files = filter_spec_files([f["filename"] for f in pr_files], patterns=doc_paths)
        if merged_spec_files:
            # Check if PR was approved
            reviews = await client.list_pull_reviews(owner, repo, pr_number)
            has_approval = any(r.get("state") == "APPROVED" for r in reviews)
            if has_approval:
                for spec_path in merged_spec_files:
                    try:
                        content, file_sha = await client.get_file_content(
                            owner, repo, spec_path, ref=base_ref
                        )
                        parse_result = parse_spec(content, ParseOptions(file_path=spec_path))
                        current_review = parse_result.document.frontmatter.review_status
                        if current_review in ("draft", "in_review", None):
                            updated_content = update_frontmatter_field(
                                content, "review_status", "approved"
                            )
                            await client.create_or_update_file(
                                owner,
                                repo,
                                spec_path,
                                updated_content,
                                f"chore(specwright): mark {spec_path} as approved",
                                file_sha,
                                branch=base_ref,
                            )
                            logger.info(
                                "Auto-advanced review_status to approved for %s (PR #%d)",
                                spec_path,
                                pr_number,
                            )
                    except Exception:
                        logger.warning(
                            "Failed to auto-advance review_status for %s",
                            spec_path,
                            exc_info=True,
                        )
    except Exception:
        logger.warning("Failed to check PR approval for review_status advancement", exc_info=True)

    # Find the bot's analysis comment
    comments = await client.list_issue_comments(owner, repo, pr_number)
    bot_comment = next(
        (c for c in comments if BOT_MARKER in c.get("body", "")),
        None,
    )
    if not bot_comment or not bot_comment.get("body"):
        return

    # Extract embedded analysis data
    analysis = extract_analysis_data(bot_comment["body"])
    if not analysis:
        return

    # Build realization insertions from embedded data
    realizations = analysis.get("realizations", [])
    realization_insertions: dict[str, list[RealizationInsertion]] = {}
    for r in realizations:
        if not isinstance(r, dict):
            continue
        status_val = r.get("status", "")
        if status_val not in ("realized", "partially_realized"):
            continue
        spec_file = r.get("spec_file", "")
        if not spec_file:
            continue
        evidence_files = r.get("evidence_files", [])
        for ef in evidence_files or [{}]:
            file_path = ef.get("path", "") if isinstance(ef, dict) else ""
            start = ef.get("start_line", "") if isinstance(ef, dict) else ""
            end = ef.get("end_line", "") if isinstance(ef, dict) else ""
            lines_str = f"{start}-{end}" if start and end else str(start) if start else ""
            realization_insertions.setdefault(spec_file, []).append(
                RealizationInsertion(
                    ac_text=r.get("ac_text", ""),
                    pr_number=pr_number,
                    file_path=file_path,
                    lines=lines_str,
                )
            )

    # Check for actionable items
    doc_updates = analysis.get("doc_updates", [])
    discrepancies = analysis.get("discrepancies", [])
    has_doc_updates = len(doc_updates) > 0
    has_realizations = len(realization_insertions) > 0
    has_conflicts = any(
        d.get("severity") == "conflict" if isinstance(d, dict) else False for d in discrepancies
    )

    if not has_doc_updates and not has_conflicts and not has_realizations:
        return

    # Need explicit doc updates or realizations to auto-generate file changes
    if not has_doc_updates and not has_realizations:
        return

    # Load specs to get current file contents
    specs = await load_repo_specs(client, owner, repo, ref=base_ref, patterns=doc_paths)

    # Build lookup of known spec file contents
    spec_contents: dict[str, str] = {}
    for s in specs:
        spec_contents[s["file_path"]] = s["raw"]

    # Apply text replacements
    file_updates: dict[str, str] = {}
    for update in doc_updates:
        spec_file = update.get("specFile") or update.get("spec_file", "")
        current_text = update.get("currentText") or update.get("current_text", "")
        suggested_text = update.get("suggestedText") or update.get("suggested_text", "")

        if spec_file in spec_contents:
            content = file_updates.get(spec_file, spec_contents[spec_file])
        elif spec_file in file_updates:
            content = file_updates[spec_file]
        else:
            # Non-spec file — fetch on demand
            try:
                raw, _sha = await client.get_file_content(owner, repo, spec_file, ref=base_ref)
                content = raw
            except Exception:
                logger.warning("Failed to fetch non-spec file for doc update: %s", spec_file)
                continue

        content = content.replace(current_text, suggested_text)
        file_updates[spec_file] = content

    # Apply realization evidence comments
    for spec_file, insertions in realization_insertions.items():
        if spec_file in spec_contents or spec_file in file_updates:
            raw = file_updates.get(spec_file, spec_contents.get(spec_file, ""))
            if raw:
                result = parse_spec(raw, ParseOptions(file_path=spec_file))
                updated = insert_realization_comments(result.document, insertions)
                if updated != raw:
                    file_updates[spec_file] = updated

    if not file_updates:
        return

    from ..client import FileChange

    branch = f"specwright/doc-update-pr-{pr_number}"
    files = [FileChange(path=path, content=content) for path, content in file_updates.items()]

    # Use "docs" instead of "specs" if any updated file is not a spec
    has_non_spec = any(not is_spec_file(path, patterns=doc_paths) for path in file_updates)
    doc_noun = "docs" if has_non_spec else "specs"

    title = f"docs: update {doc_noun} based on #{pr_number}"
    body = (
        f"Automated doc updates from merged PR #{pr_number} (`{pr['title']}`).\n\n"
        f"The Specwright agent detected changes needed:\n\n"
        + "\n".join(
            f"- **{u.get('specFile') or u.get('spec_file', '')}** "
            f"(`{u.get('sectionId') or u.get('section_id', '')}`): "
            f"{u.get('reason', '')}"
            for u in doc_updates
        )
        + "\n\n---\n_This PR was auto-created by Specwright. Please review before merging._"
    )
    commit_message = f"docs: update {doc_noun} based on #{pr_number}"

    try:
        # Check for existing open doc-update PR
        existing = await client.find_open_doc_pr(owner, repo, branch)

        if existing:
            result = await client.update_doc_pr(
                owner,
                repo,
                branch=branch,
                title=title,
                body=body,
                files=files,
                commit_message=commit_message,
                pr_number=existing.pr_number,
            )
            logger.info(
                "Updated existing doc-update PR: source=#%d doc=#%d",
                pr_number,
                existing.pr_number,
            )
        else:
            result = await client.create_doc_pr(
                owner,
                repo,
                branch=branch,
                title=title,
                body=body,
                files=files,
                commit_message=commit_message,
            )
            logger.info(
                "Created doc-update PR: source=#%d doc=#%d",
                pr_number,
                result.pr_number,
            )

        # Leave a note on the original PR
        await client.create_comment(
            owner,
            repo,
            pr_number,
            f"Specwright opened a doc-update PR: {result.pr_url}",
        )
    except Exception:
        logger.exception("Failed to create doc-update PR on merge for PR #%d", pr_number)
