"""Output tools for the autonomous debugging agent.

This module provides tools for creating output artifacts:
- Creating GitHub Pull Requests with fixes
"""

from __future__ import annotations

import asyncio
import logging
from typing import Any, Dict, Optional

from autodebug.llm_first.autonomous import BaseTool

logger = logging.getLogger(__name__)


class CreatePRTool(BaseTool):
    """Create a GitHub Pull Request with a fix.

    This tool creates a pull request on GitHub with the generated fix,
    including proper formatting, verification status, and links.
    """

    name = "create_pr"
    description = """
    Create a GitHub pull request with the fix.

    Parameters:
    - title: PR title (e.g., "fix: Resolve race condition in user_test")
    - body: PR description/body (markdown supported)
    - diff: Fix diff to apply (unified diff format)
    - branch: Branch name (e.g., "hikaflow/fix-abc123")
    - base_branch: Target branch (default: repo's default branch)
    - owner: Repository owner (if not configured)
    - repo: Repository name (if not configured)
    - run_id: Optional run ID for linking to dashboard
    - confidence: Optional confidence score to include

    Returns:
    - url: PR URL
    - number: PR number
    - branch: Branch name
    - state: PR state
    - error: Error message if failed

    Use this tool to:
    - Create a PR with a verified fix
    - Submit fix for human review
    - Complete the debugging workflow
    """

    def __init__(
        self,
        installation_id: Optional[int] = None,
        owner: Optional[str] = None,
        repo: Optional[str] = None,
        dashboard_url: str = "https://app.hikaflow.com",
    ):
        """Initialize CreatePRTool.

        Args:
            installation_id: GitHub App installation ID.
            owner: Default repository owner.
            repo: Default repository name.
            dashboard_url: Dashboard URL for links.
        """
        self.installation_id = installation_id
        self.owner = owner
        self.repo = repo
        self.dashboard_url = dashboard_url

    async def execute(
        self,
        title: str,
        body: str,
        diff: str,
        branch: str,
        base_branch: Optional[str] = None,
        owner: Optional[str] = None,
        repo: Optional[str] = None,
        run_id: Optional[str] = None,
        confidence: Optional[float] = None,
    ) -> Dict[str, Any]:
        """Create a GitHub Pull Request.

        Args:
            title: PR title.
            body: PR body/description.
            diff: Fix diff to apply.
            branch: Branch name for the fix.
            base_branch: Target branch (default: repo default).
            owner: Repository owner.
            repo: Repository name.
            run_id: Run ID for dashboard links.
            confidence: Confidence score.

        Returns:
            PR creation result.
        """
        try:
            # Use provided or default owner/repo
            repo_owner = owner or self.owner
            repo_name = repo or self.repo

            if not repo_owner or not repo_name:
                return {
                    "error": "Repository owner and name must be provided. "
                             "Set owner and repo parameters or configure defaults."
                }

            if not self.installation_id:
                return {
                    "error": "GitHub App installation ID not configured. "
                             "Cannot create PR without GitHub App access."
                }

            # Validate diff
            if not diff or not diff.strip():
                return {"error": "No diff provided"}

            # Format PR body with Hikaflow branding
            formatted_body = self._format_pr_body(
                body=body,
                diff=diff,
                run_id=run_id,
                confidence=confidence,
            )

            # Create the PR using github_app
            result = await self._create_pr(
                owner=repo_owner,
                repo=repo_name,
                title=title,
                body=formatted_body,
                diff=diff,
                branch=branch,
                base_branch=base_branch,
            )

            return result

        except Exception as e:
            logger.error(f"PR creation error: {e}")
            return {"error": f"Failed to create PR: {e}"}

    def _format_pr_body(
        self,
        body: str,
        diff: str,
        run_id: Optional[str],
        confidence: Optional[float],
    ) -> str:
        """Format PR body with Hikaflow branding.

        Args:
            body: Original body text.
            diff: Fix diff.
            run_id: Run ID for links.
            confidence: Confidence score.

        Returns:
            Formatted PR body.
        """
        parts = [
            "## Hikaflow Auto-Fix",
            "",
            body,
            "",
        ]

        if confidence is not None:
            confidence_pct = int(confidence * 100)
            emoji = "🟢" if confidence >= 0.8 else "🟡" if confidence >= 0.5 else "🔴"
            parts.extend([
                "### Confidence",
                f"{emoji} **{confidence_pct}%**",
                "",
            ])

        parts.extend([
            "### Changes",
            "```diff",
            diff[:3000],
        ])

        if len(diff) > 3000:
            parts.append("... (truncated)")

        parts.extend([
            "```",
            "",
        ])

        if run_id:
            parts.extend([
                "---",
                f"[View run details]({self.dashboard_url}/dashboard/runs/{run_id})",
                "",
            ])

        parts.extend([
            "---",
            "<sub>Generated by [Hikaflow Debugger](https://hikaflow.com) - AI-powered test debugging</sub>",
        ])

        return "\n".join(parts)

    async def _create_pr(
        self,
        owner: str,
        repo: str,
        title: str,
        body: str,
        diff: str,
        branch: str,
        base_branch: Optional[str],
    ) -> Dict[str, Any]:
        """Create PR using GitHub App.

        Args:
            owner: Repository owner.
            repo: Repository name.
            title: PR title.
            body: PR body.
            diff: Fix diff.
            branch: Branch name.
            base_branch: Target branch.

        Returns:
            PR creation result.
        """
        # Import GitHub App functions
        try:
            from api.github_app import (
                create_fix_pr,
                get_default_branch,
            )
        except ImportError:
            return {"error": "GitHub App module not available"}

        # Get default branch if not specified
        if not base_branch:
            try:
                loop = asyncio.get_event_loop()
                base_branch = await loop.run_in_executor(
                    None,
                    lambda: get_default_branch(self.installation_id, owner, repo),
                )
            except Exception as e:
                logger.warning(f"Failed to get default branch: {e}")
                base_branch = "main"

        # Build fix object for create_fix_pr
        fix = {
            "id": branch.split("-")[-1] if "-" in branch else "unknown",
            "patch": diff,
            "root_cause": title.replace("fix: ", ""),
            "explanation": body.split("\n")[0] if body else "",
            "confidence": 0.8,
            "validation_runs": 10,
        }

        try:
            # Run in thread pool since github_app functions are sync
            loop = asyncio.get_event_loop()
            result = await loop.run_in_executor(
                None,
                lambda: create_fix_pr(
                    installation_id=self.installation_id,
                    owner=owner,
                    repo=repo,
                    fix=fix,
                    run_id=branch,
                    base_branch=base_branch,
                    dashboard_url=self.dashboard_url,
                ),
            )

            return {
                "url": result.get("pr_url"),
                "number": result.get("pr_number"),
                "branch": result.get("branch"),
                "state": result.get("state", "open"),
            }

        except Exception as e:
            logger.error(f"create_fix_pr failed: {e}")
            # Fall back to simpler approach
            return await self._create_pr_simple(
                owner=owner,
                repo=repo,
                title=title,
                body=body,
                diff=diff,
                branch=branch,
                base_branch=base_branch,
            )

    async def _create_pr_simple(
        self,
        owner: str,
        repo: str,
        title: str,
        body: str,
        diff: str,
        branch: str,
        base_branch: str,
    ) -> Dict[str, Any]:
        """Simplified PR creation as fallback.

        This creates a PR by:
        1. Creating a new branch from base
        2. Applying the diff via file updates
        3. Creating the PR

        Args:
            owner: Repository owner.
            repo: Repository name.
            title: PR title.
            body: PR body.
            diff: Fix diff.
            branch: Branch name.
            base_branch: Target branch.

        Returns:
            PR creation result.
        """
        try:
            import base64
            import time

            from api.github_app import (
                apply_patch_to_content,
                create_branch,
                create_or_update_file,
                create_pull_request,
                get_branch,
                get_file_content,
                parse_patch_files,
            )

            loop = asyncio.get_event_loop()

            # Get base branch SHA
            branch_info = await loop.run_in_executor(
                None,
                lambda: get_branch(self.installation_id, owner, repo, base_branch),
            )
            base_sha = branch_info["commit"]["sha"]

            # Create unique branch name
            timestamp = int(time.time())
            branch_name = f"hikaflow/{branch}-{timestamp}"

            # Create the branch
            await loop.run_in_executor(
                None,
                lambda: create_branch(
                    self.installation_id, owner, repo, branch_name, base_sha
                ),
            )

            # Parse and apply patch files
            files_to_update = parse_patch_files(diff)

            for file_path, file_patch in files_to_update.items():
                if file_path == "unknown":
                    continue

                try:
                    # Get current file content
                    file_info = await loop.run_in_executor(
                        None,
                        lambda fp=file_path: get_file_content(
                            self.installation_id, owner, repo, fp, base_branch
                        ),
                    )

                    original_content = base64.b64decode(file_info["content"]).decode()
                    file_sha = file_info["sha"]

                    # Apply patch
                    new_content = apply_patch_to_content(original_content, file_patch)

                    # Update file
                    await loop.run_in_executor(
                        None,
                        lambda fp=file_path, nc=new_content, fs=file_sha: (
                            create_or_update_file(
                                installation_id=self.installation_id,
                                owner=owner,
                                repo=repo,
                                path=fp,
                                content=nc,
                                message=f"fix: Apply Hikaflow fix to {fp}",
                                branch=branch_name,
                                sha=fs,
                            )
                        ),
                    )

                except Exception as e:
                    logger.warning(f"Failed to update {file_path}: {e}")
                    continue

            # Create the PR
            pr = await loop.run_in_executor(
                None,
                lambda: create_pull_request(
                    installation_id=self.installation_id,
                    owner=owner,
                    repo=repo,
                    title=title,
                    body=body,
                    head=branch_name,
                    base=base_branch,
                ),
            )

            return {
                "url": pr.get("html_url"),
                "number": pr.get("number"),
                "branch": branch_name,
                "state": pr.get("state", "open"),
            }

        except Exception as e:
            logger.error(f"Simple PR creation failed: {e}")
            return {"error": f"Failed to create PR: {e}"}


class PostPRCommentTool(BaseTool):
    """Post a comment on an existing PR.

    This tool adds comments to existing pull requests, useful for
    providing fix suggestions or additional analysis.
    """

    name = "post_pr_comment"
    description = """
    Post a comment on an existing GitHub pull request.

    Parameters:
    - pr_number: The pull request number
    - body: Comment body (markdown supported)
    - owner: Repository owner (if not configured)
    - repo: Repository name (if not configured)

    Returns:
    - id: Comment ID
    - url: Comment URL
    - error: Error message if failed

    Use this tool to:
    - Add fix suggestions to existing PRs
    - Provide analysis results as comments
    """

    def __init__(
        self,
        installation_id: Optional[int] = None,
        owner: Optional[str] = None,
        repo: Optional[str] = None,
    ):
        """Initialize PostPRCommentTool.

        Args:
            installation_id: GitHub App installation ID.
            owner: Default repository owner.
            repo: Default repository name.
        """
        self.installation_id = installation_id
        self.owner = owner
        self.repo = repo

    async def execute(
        self,
        pr_number: int,
        body: str,
        owner: Optional[str] = None,
        repo: Optional[str] = None,
    ) -> Dict[str, Any]:
        """Post a comment on a PR.

        Args:
            pr_number: PR number.
            body: Comment body.
            owner: Repository owner.
            repo: Repository name.

        Returns:
            Comment creation result.
        """
        try:
            repo_owner = owner or self.owner
            repo_name = repo or self.repo

            if not repo_owner or not repo_name:
                return {"error": "Repository owner and name required"}

            if not self.installation_id:
                return {"error": "GitHub App installation ID not configured"}

            from api.github_app import create_pr_comment

            loop = asyncio.get_event_loop()
            result = await loop.run_in_executor(
                None,
                lambda: create_pr_comment(
                    installation_id=self.installation_id,
                    owner=repo_owner,
                    repo=repo_name,
                    pr_number=pr_number,
                    body=body,
                ),
            )

            return {
                "id": result.get("id"),
                "url": result.get("html_url"),
            }

        except Exception as e:
            logger.error(f"Comment creation error: {e}")
            return {"error": f"Failed to create comment: {e}"}
