"""No-op wrapper for GitHub operations."""

from pathlib import Path

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.dry_run import DryRunGitHubIssues
from erk_shared.gateway.github.issues.types import IssueInfo
from erk_shared.gateway.github.types import (
    BodyContent,
    GitHubRepoLocation,
    MergeResult,
    PRDetails,
    PRListState,
    PRNotFound,
    PRReviewThread,
    PullRequestInfo,
    WorkflowRun,
)


class DryRunGitHub(GitHub):
    """No-op wrapper for GitHub operations.

    Read operations are delegated to the wrapped implementation.
    Write operations return without executing (no-op behavior).

    This wrapper prevents destructive GitHub operations from executing in dry-run mode,
    while still allowing read operations for validation.
    """

    def __init__(self, wrapped: GitHub) -> None:
        """Initialize dry-run wrapper with a real implementation.

        Composes DryRunGitHubIssues wrapping the wrapped.issues internally.

        Args:
            wrapped: The real GitHub operations implementation to wrap
        """
        self._wrapped = wrapped
        self._dry_run_issues = DryRunGitHubIssues(wrapped.issues)

    @property
    def issues(self) -> GitHubIssues:
        """Access to issue operations (wrapped with dry-run behavior)."""
        return self._dry_run_issues

    def update_pr_base_branch(self, repo_root: Path, pr_number: int, new_base: str) -> None:
        """No-op for updating PR base branch in dry-run mode."""
        # Do nothing - prevents actual PR base update
        pass

    def update_pr_body(self, repo_root: Path, pr_number: int, body: str) -> None:
        """No-op for updating PR body in dry-run mode."""
        # Do nothing - prevents actual PR body update
        pass

    def merge_pr(
        self,
        repo_root: Path,
        pr_number: int,
        *,
        squash: bool,
        verbose: bool,
        subject: str | None = None,
        body: str | None = None,
    ) -> MergeResult:
        """No-op for merging PR in dry-run mode."""
        # Do nothing - prevents actual PR merge
        return MergeResult(pr_number=pr_number)

    def trigger_workflow(
        self, *, repo_root: Path, workflow: str, inputs: dict[str, str], ref: str | None = None
    ) -> str:
        """No-op for triggering workflow in dry-run mode.

        Returns:
            A fake run ID for dry-run mode
        """
        # Return fake run ID - prevents actual workflow trigger
        return "noop-run-12345"

    def create_pr(
        self,
        repo_root: Path,
        branch: str,
        title: str,
        body: str,
        base: str | None = None,
        *,
        draft: bool = False,
    ) -> int:
        """No-op for creating PR in dry-run mode.

        Returns:
            A sentinel value (-1) for dry-run mode
        """
        # Return sentinel value - prevents actual PR creation
        return -1

    def close_pr(self, repo_root: Path, pr_number: int) -> None:
        """No-op for closing PR in dry-run mode."""
        pass

    def list_workflow_runs(
        self, repo_root: Path, workflow: str, limit: int = 50, *, user: str | None = None
    ) -> list[WorkflowRun]:
        """Delegate read operation to wrapped implementation."""
        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:
        """Delegate read operation to wrapped implementation."""
        return self._wrapped.get_workflow_run(repo_root, run_id)

    def get_run_logs(self, repo_root: Path, run_id: str) -> str:
        """Delegate read operation to wrapped implementation."""
        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]]:
        """Delegate read operation to wrapped implementation."""
        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]:
        """Delegate read operation to wrapped implementation."""
        return self._wrapped.get_workflow_runs_by_branches(repo_root, workflow, branches)

    def poll_for_workflow_run(
        self,
        *,
        repo_root: Path,
        workflow: str,
        branch_name: str,
        timeout: int = 30,
        poll_interval: int = 2,
    ) -> str | None:
        """Delegate read operation to wrapped implementation."""
        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]:
        """Delegate read operation to wrapped implementation."""
        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]:
        """Delegate read operation to wrapped implementation."""
        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:
        """Delegate read operation to wrapped implementation."""
        return self._wrapped.get_workflow_run_node_id(repo_root, run_id)

    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]]]:
        """Delegate read operation to wrapped implementation."""
        return self._wrapped.get_issues_with_pr_linkages(
            location=location, labels=labels, state=state, limit=limit, creator=creator
        )

    def get_pr(self, repo_root: Path, pr_number: int) -> PRDetails | PRNotFound:
        """Delegate read operation to wrapped implementation."""
        return self._wrapped.get_pr(repo_root, pr_number)

    def get_pr_for_branch(self, repo_root: Path, branch: str) -> PRDetails | PRNotFound:
        """Delegate read operation to wrapped implementation."""
        return self._wrapped.get_pr_for_branch(repo_root, branch)

    def list_prs(
        self,
        repo_root: Path,
        *,
        state: PRListState,
    ) -> dict[str, PullRequestInfo]:
        """Delegate read operation to wrapped implementation."""
        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:
        """No-op for updating PR title and body in dry-run mode."""
        pass

    def mark_pr_ready(self, repo_root: Path, pr_number: int) -> None:
        """No-op for marking PR ready in dry-run mode."""
        pass

    def get_pr_diff(self, repo_root: Path, pr_number: int) -> str:
        """Delegate read operation to wrapped implementation."""
        return self._wrapped.get_pr_diff(repo_root, pr_number)

    def get_pr_changed_files(self, repo_root: Path, pr_number: int) -> list[str]:
        """Delegate read operation to wrapped implementation."""
        return self._wrapped.get_pr_changed_files(repo_root, pr_number)

    def add_label_to_pr(self, repo_root: Path, pr_number: int, label: str) -> None:
        """No-op for adding label to PR in dry-run mode."""
        pass

    def has_pr_label(self, repo_root: Path, pr_number: int, label: str) -> bool:
        """Delegate read operation to wrapped implementation."""
        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]:
        """Delegate read operation to wrapped implementation."""
        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:
        """No-op for resolving review thread in dry-run mode.

        Returns True to indicate success without actually resolving.
        """
        return True

    def add_review_thread_reply(
        self,
        repo_root: Path,
        thread_id: str,
        body: str,
    ) -> bool:
        """No-op for adding reply to review thread in dry-run mode.

        Returns True to indicate success without actually adding comment.
        """
        return True

    def create_pr_review_comment(
        self, *, repo_root: Path, pr_number: int, body: str, commit_sha: str, path: str, line: int
    ) -> int:
        """No-op for creating PR review comment in dry-run mode.

        Returns a fake comment ID to allow dry-run workflows to continue.
        """
        return 1234567890

    def find_pr_comment_by_marker(
        self,
        repo_root: Path,
        pr_number: int,
        marker: str,
    ) -> int | None:
        """Delegate to wrapped for finding comments (read-only operation)."""
        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:
        """No-op for updating PR comment in dry-run mode."""
        pass

    def create_pr_comment(
        self,
        repo_root: Path,
        pr_number: int,
        body: str,
    ) -> int:
        """No-op for creating PR comment in dry-run mode.

        Returns a fake comment ID to allow dry-run workflows to continue.
        """
        return 1234567890

    def delete_remote_branch(self, repo_root: Path, branch: str) -> bool:
        """No-op for deleting remote branch in dry-run mode.

        Returns True to indicate success without actually deleting.
        """
        return True

    def get_open_prs_with_base_branch(
        self, repo_root: Path, base_branch: str
    ) -> list[PullRequestInfo]:
        """Delegate read operation to wrapped implementation."""
        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:
        """Delegate read operation to wrapped implementation."""
        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:
        """No-op for creating gist in dry-run mode.

        Returns a fake GistCreated to allow dry-run workflows to continue.
        """
        return GistCreated(
            gist_id="dry-run-gist-id",
            gist_url="https://gist.github.com/dry-run/dry-run-gist-id",
            raw_url=f"https://gist.githubusercontent.com/dry-run/dry-run-gist-id/raw/{filename}",
        )

    def create_commit_status(
        self,
        *,
        repo: str,
        sha: str,
        state: str,
        context: str,
        description: str,
    ) -> bool:
        """No-op for creating commit status in dry-run mode.

        Returns True to indicate success without actually creating status.
        """
        return True
