"""Generate GitHub Pull Requests with IaC drift remediation code."""

from __future__ import annotations

import json
import logging
import os
import subprocess
import tempfile
import time
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Optional

from .remediation import RemediationPlan

logger = logging.getLogger(__name__)


@dataclass
class PRResult:
    """Result of creating a PR."""
    success: bool
    pr_url: Optional[str] = None
    pr_number: Optional[int] = None
    branch_name: Optional[str] = None
    error: Optional[str] = None
    files_changed: list[str] = field(default_factory=list)

    def to_dict(self) -> dict[str, Any]:
        return {
            "success": self.success,
            "pr_url": self.pr_url,
            "pr_number": self.pr_number,
            "branch_name": self.branch_name,
            "error": self.error,
            "files_changed": self.files_changed,
        }


class PRGenerator:
    """Generate GitHub PRs with IaC remediation code.

    Uses the `gh` CLI or the GitHub API to create pull requests
    containing auto-generated IaC fixes for detected drift.
    """

    def __init__(
        self,
        repo_path: str | Path = ".",
        github_token: Optional[str] = None,
    ) -> None:
        self.repo_path = Path(repo_path).resolve()
        self.github_token = github_token or os.environ.get("GITHUB_TOKEN", "")

    def create_pr(
        self,
        plans: list[RemediationPlan],
        scan_id: str,
        base_branch: str = "main",
        dry_run: bool = False,
    ) -> PRResult:
        """Create a GitHub PR with remediation code.

        Args:
            plans: List of remediation plans to include.
            scan_id: Scan ID for branch naming.
            base_branch: Base branch to create PR against.
            dry_run: If True, generate files but don't push or create PR.

        Returns:
            PRResult with PR URL and metadata.
        """
        if not plans:
            return PRResult(success=False, error="No remediation plans provided")

        branch_name = f"driftguard/fix-{scan_id[:12]}"
        files_changed: list[str] = []

        try:
            # 1. Create branch
            if not dry_run:
                self._run_git(["checkout", "-b", branch_name])

            # 2. Write remediation files
            for i, plan in enumerate(plans):
                file_path = self._write_remediation_file(plan, i)
                files_changed.append(str(file_path))

            # 3. Write summary file
            summary_path = self._write_summary(plans, scan_id)
            files_changed.append(str(summary_path))

            if dry_run:
                return PRResult(
                    success=True,
                    branch_name=branch_name,
                    files_changed=files_changed,
                )

            # 4. Commit
            self._run_git(["add"] + files_changed)
            commit_msg = self._build_commit_message(plans, scan_id)
            self._run_git(["commit", "-m", commit_msg])

            # 5. Push
            self._run_git(["push", "-u", "origin", branch_name])

            # 6. Create PR
            pr_title = f"[DriftGuard] Fix {len(plans)} drift(s) from scan {scan_id[:8]}"
            pr_body = self._build_pr_body(plans, scan_id)

            result = subprocess.run(
                ["gh", "pr", "create",
                 "--title", pr_title,
                 "--body", pr_body,
                 "--base", base_branch,
                 "--head", branch_name],
                cwd=str(self.repo_path),
                capture_output=True, text=True, timeout=30,
            )

            if result.returncode != 0:
                return PRResult(
                    success=False,
                    branch_name=branch_name,
                    files_changed=files_changed,
                    error=f"gh pr create failed: {result.stderr}",
                )

            # Parse PR URL from output
            pr_url = result.stdout.strip()
            pr_number = None
            if "/" in pr_url:
                try:
                    pr_number = int(pr_url.rstrip("/").split("/")[-1])
                except ValueError:
                    pass

            return PRResult(
                success=True,
                pr_url=pr_url,
                pr_number=pr_number,
                branch_name=branch_name,
                files_changed=files_changed,
            )

        except Exception as e:
            logger.error("PR creation failed: %s", e)
            # Cleanup: switch back to base branch
            try:
                self._run_git(["checkout", base_branch])
            except Exception:
                pass
            return PRResult(success=False, error=str(e), files_changed=files_changed)

    def _write_remediation_file(self, plan: RemediationPlan, index: int) -> Path:
        """Write a remediation plan to a file."""
        fix_dir = self.repo_path / "driftguard-fixes"
        fix_dir.mkdir(exist_ok=True)

        name = plan.resource_address.replace(".", "_").replace("/", "_").replace("::", "_")
        if plan.iac_format == "terraform":
            file_path = fix_dir / f"fix_{index:03d}_{name}.tf"
        else:
            file_path = fix_dir / f"fix_{index:03d}_{name}.py"

        file_path.write_text(plan.code, encoding="utf-8")
        return file_path

    def _write_summary(self, plans: list[RemediationPlan], scan_id: str) -> Path:
        """Write a summary of all fixes."""
        fix_dir = self.repo_path / "driftguard-fixes"
        fix_dir.mkdir(exist_ok=True)
        summary_path = fix_dir / "DRIFT_FIX_SUMMARY.md"

        lines = [
            f"# DriftGuard Remediation Summary",
            f"",
            f"**Scan ID:** {scan_id}",
            f"**Generated:** {time.strftime('%Y-%m-%d %H:%M:%S UTC', time.gmtime())}",
            f"**Fixes:** {len(plans)}",
            f"",
            f"## Remediation Plans",
            f"",
        ]

        for i, plan in enumerate(plans):
            risk_badge = {"low": "LOW", "medium": "MEDIUM", "high": "HIGH"}.get(plan.risk_level, "?")
            lines.append(f"### {i+1}. {plan.resource_address}")
            lines.append(f"")
            lines.append(f"- **Type:** {plan.resource_type}")
            lines.append(f"- **Risk:** {risk_badge}")
            lines.append(f"- **Format:** {plan.iac_format}")
            lines.append(f"- **Description:** {plan.description}")
            if plan.requires_import:
                lines.append(f"- **Import command:** `{plan.import_command}`")
            if plan.attribute_fixes:
                lines.append(f"- **Attribute fixes:**")
                for fix in plan.attribute_fixes:
                    lines.append(f"  - {fix}")
            lines.append(f"")

        lines.append("---")
        lines.append("Generated by [DriftGuard](https://autoailabs.co.uk/products/driftguard)")

        summary_path.write_text("\n".join(lines), encoding="utf-8")
        return summary_path

    def _build_commit_message(self, plans: list[RemediationPlan], scan_id: str) -> str:
        """Build a git commit message."""
        resource_types = set(p.resource_type for p in plans)
        return (
            f"fix(drift): remediate {len(plans)} drift(s) from scan {scan_id[:8]}\n\n"
            f"Resources: {', '.join(sorted(resource_types))}\n"
            f"Generated by DriftGuard"
        )

    def _build_pr_body(self, plans: list[RemediationPlan], scan_id: str) -> str:
        """Build GitHub PR body."""
        high_risk = sum(1 for p in plans if p.risk_level == "high")
        medium_risk = sum(1 for p in plans if p.risk_level == "medium")
        low_risk = sum(1 for p in plans if p.risk_level == "low")

        body = f"""## DriftGuard Drift Remediation

**Scan ID:** `{scan_id}`
**Total fixes:** {len(plans)}
**Risk breakdown:** {high_risk} high, {medium_risk} medium, {low_risk} low

### Changes
"""
        for plan in plans:
            body += f"- **{plan.resource_address}** ({plan.resource_type}): {plan.description}\n"

        body += """
### Review Checklist
- [ ] Verify each fix is appropriate for the environment
- [ ] Check that no intentional changes are being reverted
- [ ] Run `terraform plan` / `pulumi preview` before merging
- [ ] Confirm no breaking changes to dependent resources

---
*Generated by [DriftGuard](https://autoailabs.co.uk/products/driftguard)*
"""
        return body

    def _run_git(self, args: list[str]) -> str:
        """Run a git command in the repo directory."""
        result = subprocess.run(
            ["git"] + args,
            cwd=str(self.repo_path),
            capture_output=True, text=True, timeout=30,
        )
        if result.returncode != 0:
            raise RuntimeError(f"git {' '.join(args)} failed: {result.stderr}")
        return result.stdout
