"""Printing wrapper for GitHub operations."""

from pathlib import Path

import click

from erk_shared.gateway.github.abc import GistCreated, GistCreateError, GitHub
from erk_shared.gateway.github.issues.abc import GitHubIssues
from erk_shared.gateway.github.issues.types import IssueInfo
from erk_shared.gateway.github.types import (
    BodyContent,
    BodyFile,
    BodyText,
    GitHubRepoLocation,
    MergeError,
    MergeResult,
    PRDetails,
    PRListState,
    PRNotFound,
    PRReviewThread,
    PullRequestInfo,
    WorkflowRun,
)
from erk_shared.printing.base import PrintingBase


class PrintingGitHub(PrintingBase, GitHub):
    """Wrapper that prints operations before delegating to inner implementation.

    This wrapper prints styled output for operations, then delegates to the
    wrapped implementation (which could be Real or Noop).

    Usage:
        # For production
        printing_ops = PrintingGitHub(real_ops, script_mode=False, dry_run=False)

        # For dry-run
        noop_inner = DryRunGitHub(real_ops)
        printing_ops = PrintingGitHub(noop_inner, script_mode=False, dry_run=True)
    """

    # Inherits __init__, _emit, and _format_command from PrintingBase

    @property
    def issues(self) -> GitHubIssues:
        """Access to issue operations (delegates to wrapped)."""
        return self._wrapped.issues

    # Read-only operations: delegate without printing

    def list_workflow_runs(
        self, repo_root: Path, workflow: str, limit: int = 50, *, user: str | None = None
    ) -> list[WorkflowRun]:
        """List workflow runs (read-only, no printing)."""
        return self._wrapped.list_workflow_runs(repo_root, workflow, limit, user=user)

    def get_workflow_run(self, repo_root: Path, run_id: str) -> WorkflowRun | None:
        """Get workflow run details (read-only, no printing)."""
        return self._wrapped.get_workflow_run(repo_root, run_id)

    def get_run_logs(self, repo_root: Path, run_id: str) -> str:
        """Get run logs (read-only, no printing)."""
        return self._wrapped.get_run_logs(repo_root, run_id)

    def get_prs_linked_to_issues(
        self,
        location: GitHubRepoLocation,
        issue_numbers: list[int],
    ) -> dict[int, list[PullRequestInfo]]:
        """Get PRs linked to issues (read-only, no printing)."""
        return self._wrapped.get_prs_linked_to_issues(location, issue_numbers)

    def get_workflow_runs_by_branches(
        self, repo_root: Path, workflow: str, branches: list[str]
    ) -> dict[str, WorkflowRun | None]:
        """Get workflow runs by branches (read-only, no printing)."""
        return self._wrapped.get_workflow_runs_by_branches(repo_root, workflow, branches)

    # Operations that need printing

    def update_pr_base_branch(self, repo_root: Path, pr_number: int, new_base: str) -> None:
        """Update PR base branch with printed output."""
        self._emit(self._format_command(f"gh pr edit {pr_number} --base {new_base}"))
        self._wrapped.update_pr_base_branch(repo_root, pr_number, new_base)

    def update_pr_body(self, repo_root: Path, pr_number: int, body: str) -> None:
        """Update PR body with printed output."""
        self._emit(self._format_command(f"gh pr edit {pr_number} --body <body>"))
        self._wrapped.update_pr_body(repo_root, pr_number, body)

    def merge_pr(
        self,
        repo_root: Path,
        pr_number: int,
        *,
        squash: bool,
        verbose: bool,
        subject: str | None = None,
        body: str | None = None,
    ) -> MergeResult | MergeError:
        """Merge PR with printed output."""
        merge_type = "--squash" if squash else "--merge"
        self._emit(self._format_command(f"gh pr merge {pr_number} {merge_type}"))
        return self._wrapped.merge_pr(
            repo_root, pr_number, squash=squash, verbose=verbose, subject=subject, body=body
        )

    def trigger_workflow(
        self, *, repo_root: Path, workflow: str, inputs: dict[str, str], ref: str | None = None
    ) -> str:
        """Trigger workflow with printed output.

        Returns:
            The GitHub Actions run ID as a string
        """
        ref_arg = f"--ref {ref} " if ref else ""
        input_args = " ".join(f"-f {key}={value}" for key, value in inputs.items())
        self._emit(self._format_command(f"gh workflow run {workflow} {ref_arg}{input_args}"))
        self._emit(f"   Polling for run (max {15} attempts)...")
        run_id = self._wrapped.trigger_workflow(
            repo_root=repo_root, workflow=workflow, inputs=inputs, ref=ref
        )
        self._emit(f"-> Run ID: {click.style(run_id, fg='green')}")
        return run_id

    def create_pr(
        self,
        repo_root: Path,
        branch: str,
        title: str,
        body: str,
        base: str | None = None,
        *,
        draft: bool = False,
    ) -> int:
        """Create PR with printed output.

        Returns:
            PR number
        """
        draft_arg = "--draft " if draft else ""
        base_arg = f"--base {base} " if base is not None else ""
        self._emit(
            self._format_command(
                f'gh pr create --head {branch} {draft_arg}{base_arg}--title "{title}" --body <body>'
            )
        )
        pr_number = self._wrapped.create_pr(repo_root, branch, title, body, base=base, draft=draft)
        self._emit(f"-> PR #{pr_number}")
        return pr_number

    def close_pr(self, repo_root: Path, pr_number: int) -> None:
        """Close PR with printed output."""
        self._emit(self._format_command(f"gh pr close {pr_number}"))
        self._wrapped.close_pr(repo_root, pr_number)

    def poll_for_workflow_run(
        self,
        *,
        repo_root: Path,
        workflow: str,
        branch_name: str,
        timeout: int = 30,
        poll_interval: int = 2,
    ) -> str | None:
        """Poll for workflow run (read-only, no printing)."""
        return self._wrapped.poll_for_workflow_run(
            repo_root=repo_root,
            workflow=workflow,
            branch_name=branch_name,
            timeout=timeout,
            poll_interval=poll_interval,
        )

    def check_auth_status(self) -> tuple[bool, str | None, str | None]:
        """Check auth status (read-only, no printing)."""
        return self._wrapped.check_auth_status()

    def get_workflow_runs_by_node_ids(
        self,
        repo_root: Path,
        node_ids: list[str],
    ) -> dict[str, WorkflowRun | None]:
        """Get workflow runs by node IDs (read-only, no printing)."""
        return self._wrapped.get_workflow_runs_by_node_ids(repo_root, node_ids)

    def get_workflow_run_node_id(self, repo_root: Path, run_id: str) -> str | None:
        """Get workflow run node ID (read-only, no printing)."""
        return self._wrapped.get_workflow_run_node_id(repo_root, run_id)

    def get_pr(self, repo_root: Path, pr_number: int) -> PRDetails | PRNotFound:
        """Get comprehensive PR details (read-only, no printing)."""
        return self._wrapped.get_pr(repo_root, pr_number)

    def get_pr_for_branch(self, repo_root: Path, branch: str) -> PRDetails | PRNotFound:
        """Get comprehensive PR details for a branch (read-only, no printing)."""
        return self._wrapped.get_pr_for_branch(repo_root, branch)

    def list_prs(
        self,
        repo_root: Path,
        *,
        state: PRListState,
    ) -> dict[str, PullRequestInfo]:
        """List PRs for the repository (read-only, no printing)."""
        return self._wrapped.list_prs(repo_root, state=state)

    def update_pr_title_and_body(
        self, *, repo_root: Path, pr_number: int, title: str, body: BodyContent
    ) -> None:
        """Update PR title and body with printed output."""
        if isinstance(body, BodyFile):
            self._emit(
                self._format_command(
                    f"gh pr edit {pr_number} --title <title> --body-file {body.path}"
                )
            )
        elif isinstance(body, BodyText):
            self._emit(
                self._format_command(f"gh pr edit {pr_number} --title <title> --body <body>")
            )
        self._wrapped.update_pr_title_and_body(
            repo_root=repo_root, pr_number=pr_number, title=title, body=body
        )

    def mark_pr_ready(self, repo_root: Path, pr_number: int) -> None:
        """Mark PR as ready with printed output."""
        self._emit(self._format_command(f"gh pr ready {pr_number}"))
        self._wrapped.mark_pr_ready(repo_root, pr_number)

    def get_pr_diff(self, repo_root: Path, pr_number: int) -> str:
        """Get PR diff (read-only, no printing)."""
        return self._wrapped.get_pr_diff(repo_root, pr_number)

    def get_pr_changed_files(self, repo_root: Path, pr_number: int) -> list[str]:
        """Get list of files changed in PR (read-only, no printing)."""
        return self._wrapped.get_pr_changed_files(repo_root, pr_number)

    def get_issues_with_pr_linkages(
        self,
        *,
        location: GitHubRepoLocation,
        labels: list[str],
        state: str | None = None,
        limit: int | None = None,
        creator: str | None = None,
    ) -> tuple[list[IssueInfo], dict[int, list[PullRequestInfo]]]:
        """Get issues with PR linkages (read-only, no printing)."""
        return self._wrapped.get_issues_with_pr_linkages(
            location=location, labels=labels, state=state, limit=limit, creator=creator
        )

    def add_label_to_pr(self, repo_root: Path, pr_number: int, label: str) -> None:
        """Add label to PR with printed output."""
        self._emit(self._format_command(f"gh pr edit {pr_number} --add-label {label}"))
        self._wrapped.add_label_to_pr(repo_root, pr_number, label)

    def has_pr_label(self, repo_root: Path, pr_number: int, label: str) -> bool:
        """Check if PR has label (read-only, no printing)."""
        return self._wrapped.has_pr_label(repo_root, pr_number, label)

    def get_pr_review_threads(
        self,
        repo_root: Path,
        pr_number: int,
        *,
        include_resolved: bool = False,
    ) -> list[PRReviewThread]:
        """Get review threads for a pull request (read-only, no printing)."""
        return self._wrapped.get_pr_review_threads(
            repo_root, pr_number, include_resolved=include_resolved
        )

    def resolve_review_thread(self, repo_root: Path, thread_id: str) -> bool:
        """Resolve a PR review thread with printed output."""
        self._emit(self._format_command(f"gh api graphql (resolve thread {thread_id})"))
        return self._wrapped.resolve_review_thread(repo_root, thread_id)

    def add_review_thread_reply(self, repo_root: Path, thread_id: str, body: str) -> bool:
        """Add a reply to a PR review thread with printed output."""
        self._emit(self._format_command(f"gh api graphql (add reply to thread {thread_id})"))
        return self._wrapped.add_review_thread_reply(repo_root, thread_id, body)

    def create_pr_review_comment(
        self, *, repo_root: Path, pr_number: int, body: str, commit_sha: str, path: str, line: int
    ) -> int:
        """Create PR review comment with printed output."""
        self._emit(
            self._format_command(f"gh api repos/.../pulls/{pr_number}/comments (line {line})")
        )
        return self._wrapped.create_pr_review_comment(
            repo_root=repo_root,
            pr_number=pr_number,
            body=body,
            commit_sha=commit_sha,
            path=path,
            line=line,
        )

    def find_pr_comment_by_marker(
        self,
        repo_root: Path,
        pr_number: int,
        marker: str,
    ) -> int | None:
        """Find PR comment by marker (read-only, no print)."""
        return self._wrapped.find_pr_comment_by_marker(repo_root, pr_number, marker)

    def update_pr_comment(
        self,
        repo_root: Path,
        comment_id: int,
        body: str,
    ) -> None:
        """Update PR comment with printed output."""
        self._emit(self._format_command(f"gh api PATCH .../comments/{comment_id}"))
        self._wrapped.update_pr_comment(repo_root, comment_id, body)

    def create_pr_comment(
        self,
        repo_root: Path,
        pr_number: int,
        body: str,
    ) -> int:
        """Create PR comment with printed output."""
        self._emit(self._format_command(f"gh pr comment {pr_number}"))
        return self._wrapped.create_pr_comment(repo_root, pr_number, body)

    def delete_remote_branch(self, repo_root: Path, branch: str) -> bool:
        """Delete remote branch with printed output."""
        self._emit(self._format_command(f"gh api DELETE .../git/refs/heads/{branch}"))
        return self._wrapped.delete_remote_branch(repo_root, branch)

    def get_open_prs_with_base_branch(
        self, repo_root: Path, base_branch: str
    ) -> list[PullRequestInfo]:
        """Get open PRs with base branch (read-only, no printing)."""
        return self._wrapped.get_open_prs_with_base_branch(repo_root, base_branch)

    def download_run_artifact(
        self,
        repo_root: Path,
        run_id: str,
        artifact_name: str,
        destination: Path,
    ) -> bool:
        """Download artifact (read-only, no printing)."""
        return self._wrapped.download_run_artifact(repo_root, run_id, artifact_name, destination)

    def create_gist(
        self,
        *,
        filename: str,
        content: str,
        description: str,
        public: bool,
    ) -> GistCreated | GistCreateError:
        """Create gist with printed output."""
        public_flag = "--public" if public else ""
        self._emit(self._format_command(f"gh gist create --filename {filename} {public_flag}"))
        result = self._wrapped.create_gist(
            filename=filename, content=content, description=description, public=public
        )
        if isinstance(result, GistCreated):
            self._emit(f"-> {click.style(result.gist_url, fg='green')}")
        return result

    def create_commit_status(
        self,
        *,
        repo: str,
        sha: str,
        state: str,
        context: str,
        description: str,
    ) -> bool:
        """Create commit status with printed output."""
        self._emit(
            self._format_command(f"gh api repos/{repo}/statuses/{sha[:8]}... -f state={state}")
        )
        return self._wrapped.create_commit_status(
            repo=repo, sha=sha, state=state, context=context, description=description
        )
