import os
import re
import requests
import gitlab
from dotenv import load_dotenv
from mcp.server.fastmcp import FastMCP

load_dotenv()

mcp = FastMCP("OpenGitAnalyst")
GITLAB_GRAPHQL_URL = "https://gitlab.com/api/graphql"
ALLOWED_AUTONOMOUS_FILES = {
    "requirements.txt", 
    ".opengit/memory/runbook.md",
    "LICENSE",
    ".gitignore",
    ".gitlab-ci.yml",
    "src/app.py",
    ".env.example",
    ".opengit/memory/last_audit.json",
    ".opengit/memory/sci_history.json"
}


def _get_token() -> str:
    token = os.environ.get("GITLAB_TOKEN", "").strip()
    if not token:
        raise RuntimeError("GITLAB_TOKEN is required")
    return token


def _gitlab_client() -> gitlab.Gitlab:
    return gitlab.Gitlab("https://gitlab.com", private_token=_get_token())


@mcp.tool()
async def create_or_update_file_branch(project_path: str, branch: str, commit_message: str, file_path: str, content: str, start_branch: str = "main") -> dict:
    # Guardrail: Allow specific files or anything in the memory directory
    if file_path not in ALLOWED_AUTONOMOUS_FILES and not file_path.startswith(".opengit/memory/"):
        raise RuntimeError(f"Autonomous write denied for file: {file_path}")

    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    try:
        proj.branches.get(branch)
    except Exception:
        proj.branches.create({"branch": branch, "ref": start_branch})

    actions = [{"action": "update", "file_path": file_path, "content": content}]
    try:
        proj.files.get(file_path=file_path, ref=branch)
    except Exception:
        actions[0]["action"] = "create"

    commit = proj.commits.create({"branch": branch, "commit_message": commit_message, "actions": actions})
    return {"branch": branch, "commit_id": commit.id, "status": "committed"}


@mcp.tool()
async def audit_repository(project_path: str, ref: str = "main") -> list[dict]:
    """
    Performs a multi-dimensional security & compliance audit of the repository.
    Scenarios: exposed_secret, missing_license, missing_gitignore, hardcoded_ip, missing_ci_tests, dependency_drift.
    """
    findings = []
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)

    # 1. Dependency Drift (Existing)
    drift = await scan_repo_for_drift(project_path, ref)
    if drift["detected"]:
        findings.append({"scenario": "dependency_drift", "package": drift["package"], "spec": drift["required_spec"], "content": drift.get("current_content", ""), "reason": drift["reason"]})

    # 2. Exposed Secrets in .env.example
    env_ex = await get_file_content(project_path, ".env.example", ref)
    if env_ex["found"]:
        # Look for things that look like real tokens vs placeholders
        content = env_ex["content"]
        # Basic regex for glpat (GitLab), apikey, etc.
        if re.search(r"glpat-[0-9a-zA-Z\-]{20,}", content) or re.search(r"=[a-zA-Z0-9]{32,}(\s|$)", content):
             findings.append({
                 "scenario": "exposed_secret",
                 "file": ".env.example",
                 "content": content,
                 "reason": "Found potentially active credentials committed in .env.example"
             })

    # 3. Missing LICENSE
    license_check = await get_file_content(project_path, "LICENSE", ref)
    if not license_check["found"]:
        findings.append({
            "scenario": "missing_license",
            "reason": "No LICENSE file found in root directory. Repository is in legal limbo."
        })

    # 4. Missing .gitignore
    ignore_check = await get_file_content(project_path, ".gitignore", ref)
    if not ignore_check["found"]:
        findings.append({
            "scenario": "missing_gitignore",
            "reason": "No .gitignore file found. High risk of committing local configs and build artifacts."
        })

    # 5. Missing CI Test Stage
    ci_check = await get_file_content(project_path, ".gitlab-ci.yml", ref)
    if ci_check["found"]:
        if "test" not in ci_check["content"].lower() or "pytest" not in ci_check["content"].lower():
             findings.append({
                 "scenario": "missing_ci_tests",
                 "content": ci_check["content"],
                 "reason": "Pipeline exists but has no visible 'test' stage or 'pytest' invocation."
             })

    # 6. Hardcoded IP (SAST)
    app_py = await get_file_content(project_path, "src/app.py", ref)
    if app_py["found"] and (re.search(r"=\s*['\"]127\.0\.0\.1['\"]", app_py["content"]) or re.search(r"=\s*['\"]0\.0\.0\.0['\"]", app_py["content"])):
         findings.append({
             "scenario": "hardcoded_ip",
             "file": "src/app.py",
             "content": app_py["content"],
             "reason": "Hardcoded loopback IP address found in application source."
         })

    return findings


@mcp.tool()
async def get_fix_template(scenario: str, original_content: str = "") -> str:
    """Returns the fixed content for a detected scenario."""
    templates = {
        "missing_license": "MIT License\n\nCopyright (c) 2026 OpenGit Contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy...",
        "missing_gitignore": ".env\n__pycache__/\n*.pyc\nvenv/\n.opengit/state.json\n.pytest_cache/\ndist/\nbuild/\n",
        "exposed_secret": re.sub(r"=([a-zA-Z0-9\-_]{15,})", "=YOUR_TOKEN_HERE", original_content),
        "missing_ci_tests": original_content + "\n\ntest_job:\n  stage: test\n  script:\n    - pytest\n",
        "hardcoded_ip": original_content.replace("127.0.0.1", "os.environ.get('HOST', '0.0.0.0')"),
    }
    if scenario == "dependency_drift":
        patched = await upsert_requests_policy(original_content)
        return patched["content"]
    return templates.get(scenario, original_content)


@mcp.tool()
async def detect_dependency_drift(log_tail: str) -> dict:
    text = log_tail.lower()
    if "modulenotfounderror" in text and "requests" in text:
        return {"detected": True, "kind": "missing_dependency", "package": "requests", "required_spec": "requests>=2.31.0", "reason": "ModuleNotFoundError indicates missing requests import dependency"}
    req_re = re.search(r"requests(?:==|>=|<=|~=|!=)\s*([0-9][^\s'\"]+)", text)
    if req_re and "2.31.0" not in text:
        return {"detected": True, "kind": "outdated_dependency", "package": "requests", "required_spec": "requests>=2.31.0", "reason": "requests version in logs appears below policy"}
    return {"detected": False, "reason": "No supported deterministic drift pattern found"}


@mcp.tool()
async def upsert_requests_policy(requirements_content: str) -> dict:
    lines = requirements_content.splitlines()
    out = []
    found = False
    for line in lines:
        stripped = line.strip()
        if stripped.lower().startswith("requests") and not stripped.startswith("#"):
            out.append("requests>=2.31.0")
            found = True
        else:
            out.append(line)
    if not found:
        out.append("requests>=2.31.0")
    return {"updated": True, "content": "\n".join(out) + "\n"}


@mcp.tool()
async def get_latest_failed_job(project_path: str, ref: str = "main") -> dict:
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    pipelines = proj.pipelines.list(ref=ref, status="failed", per_page=20)
    if not pipelines:
        raise RuntimeError(f"No failed pipelines found for {project_path} on ref '{ref}'")
    for p in pipelines:
        jobs = proj.jobs.list(pipeline_id=p.id, scope=["failed"], per_page=100)
        if jobs:
            job = jobs[0]
            return {"pipeline_id": p.id, "job_id": job.id, "job_name": job.name, "job_web_url": job.web_url}
    raise RuntimeError("Failed pipelines exist, but no failed jobs were found")


@mcp.tool()
async def get_failed_job_logs(project_path: str, job_id: int, tail_lines: int = 200) -> dict:
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    job = proj.jobs.get(job_id)
    trace = job.trace().decode("utf-8", errors="replace")
    return {"job_id": job_id, "log_tail": "\n".join(trace.splitlines()[-tail_lines:]), "status": "success"}


@mcp.tool()
async def create_issue(project_path: str, title: str, description: str, issue_type: str = "issue", labels: list[str] = None, milestone_id: int = None, severity: str = "medium") -> dict:
    """
    Creates a GitLab Issue with optional SRE Incident formatting.
    Enhanced for Polish: Implements intelligent deduplication by checking for existing open issues with same title.
    """
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    
    # ── Tier 6: Intelligent Issue Deduplication ────────────────────────────
    # Search for existing open incidents with similar titles
    clean_title = title.replace("[OpenGit]", "").strip()
    existing_issues = proj.issues.list(state='opened', per_page=50)
    
    for existing in existing_issues:
        if clean_title in existing.title or existing.title in title:
            print(f"  ℹ️  Incident already exists: #{existing.iid} (Title: {existing.title})")
            # If multiple open, the cleanup script (scripts/cleanup_duplicates.py) will consolidate them
            return {
                "issue_id": f"#{existing.iid}",
                "url": existing.web_url,
                "status": "already_exists"
            }
    
    # Create new incident if no active match found
    final_desc = description
    if issue_type == "incident" and severity == "high":
        final_desc += "\n\n## 🚨 OpenGit War Room Protocol\n- **Status**: ACTIVE\n- **Command**: `opengit war-room --issue "

    payload = {
        "title": f"[OpenGit] {title}" if "[OpenGit]" not in title else title,
        "description": final_desc,
        "issue_type": issue_type,
        "labels": labels or ["opengit"]
    }
    if milestone_id:
        payload["milestone_id"] = milestone_id
        
    issue = proj.issues.create(payload)
    
    if issue_type == "incident" and severity == "high":
        issue.description += f"{issue.iid}`\n"
        issue.save()
        
    return {"issue_id": f"#{issue.iid}", "url": issue.web_url, "status": "created"}


@mcp.tool()
async def create_incident(project_path: str, title: str, description: str, severity: str = "critical", labels: list[str] = None, milestone_id: int = None) -> dict:
    """Creates a GitLab Incident with automated War Room triggers."""
    return await create_issue(
        project_path, 
        f"[OpenGit] {title}", 
        f"Severity: {severity.upper()}\n\n{description}", 
        issue_type="incident", 
        labels=labels, 
        milestone_id=milestone_id,
        severity="high" if severity.lower() in ["critical", "high"] else "medium"
    )


@mcp.tool()
async def generate_gl_security_report(project_path: str, findings: list) -> dict:
    """
    Converts OpenGit findings into GitLab-native Security Report artifacts (SAST/Secret Detection).
    Enables results to appear in the GitLab Security Dashboard.
    """
    import uuid
    from datetime import datetime
    
    vulnerabilities = []
    for f in findings:
        vuln_id = str(uuid.uuid4())
        vuln = {
            "id": vuln_id,
            "category": "sast" if f["scenario"] != "exposed_secret" else "secret_detection",
            "name": f["scenario"].replace("_", " ").title(),
            "message": f["reason"],
            "severity": "Critical" if f.get("scenario") == "exposed_secret" else "Medium",
            "scanner": {"id": "opengit_sre", "name": "OpenGit SRE"},
            "location": {
                "file": f.get("file", "unknown"),
                "start_line": 1
            },
            "identifiers": [
                {"type": "opengit_rule_id", "name": f["scenario"], "value": f["scenario"], "url": "https://gitlab.com/opengit/sre"}
            ]
        }
        vulnerabilities.append(vuln)
        
    report = {
        "version": "15.0.0",
        "vulnerabilities": vulnerabilities,
        "remediation": []
    }
    
    return {"report_type": "security", "content": report, "filename": "gl-sast-report.json"}


@mcp.tool()
async def get_file_content(project_path: str, file_path: str, ref: str = "main") -> dict:
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    try:
        f = proj.files.get(file_path=file_path, ref=ref)
        return {"content": f.decode().decode("utf-8"), "found": True}
    except Exception:
        return {"content": "", "found": False}

@mcp.tool()
async def scan_repo_for_drift(project_path: str, ref: str = "main") -> dict:
    res = await get_file_content(project_path, "requirements.txt", ref)
    if not res["found"]:
         return {"detected": False, "reason": "No requirements.txt found"}
    
    content = res["content"].lower()
    if "requests" in content and "requests>=2.31.0" not in content:
        return {
            "detected": True,
            "kind": "proactive_outdated_dependency",
            "package": "requests",
            "required_spec": "requests>=2.31.0",
            "reason": "Policy requires requests>=2.31.0 to mitigate CVE-2023-44487. Repo has older spec.",
            "current_content": res["content"]
        }
    return {"detected": False, "reason": "Requirements meet the current security policy."}

@mcp.tool()
async def create_doctor_mr(project: str, source_branch: str, target_branch: str, title: str, description: str, labels: list[str] = None, milestone_id: int = None) -> dict:
    gl = _gitlab_client()
    proj = gl.projects.get(project)
    
    try:
        mrs = proj.mergerequests.list(state='opened', source_branch=source_branch, target_branch=target_branch)
        if mrs:
             return {"mr_id": f"!{mrs[0].iid}", "url": mrs[0].web_url, "status": "already_exists"}
    except Exception:
        pass

    payload = {
        "source_branch": source_branch, 
        "target_branch": target_branch, 
        "title": f"🩺 [OpenGit] {title}", 
        "description": description, 
        "remove_source_branch": True
    }
    if labels:
        payload["labels"] = labels
    if milestone_id:
        payload["milestone_id"] = milestone_id

    mr = proj.mergerequests.create(payload)
    return {"mr_id": f"!{mr.iid}", "url": mr.web_url, "status": "created"}

@mcp.tool()
async def publish_wiki_report(project_path: str, title: str, content: str) -> dict:
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    try:
        proj.wikis.create({'title': title, 'content': content, 'format': 'markdown'})
        return {"status": "published", "title": title}
    except Exception as e:
        return {"status": "error", "message": str(e)}

@mcp.tool()
async def ensure_label(project_path: str, name: str, color: str, description: str = "") -> dict:
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    try:
        proj.labels.get(name)
        return {"status": "exists", "name": name}
    except Exception:
        proj.labels.create({'name': name, 'color': color, 'description': description})
        return {"status": "created", "name": name}

@mcp.tool()
async def ensure_milestone(project_path: str, title: str, description: str = "") -> dict:
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    try:
        ms = proj.milestones.list(title=title)
        if ms:
            return {"status": "exists", "id": ms[0].id}
        m = proj.milestones.create({'title': title, 'description': description})
        return {"status": "created", "id": m.id}
    except Exception as e:
        return {"status": "error", "message": str(e)}

@mcp.tool()
async def create_release(project_path: str, tag_name: str, name: str, description: str) -> dict:
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    try:
        rel = proj.releases.create({'name': name, 'tag_name': tag_name, 'description': description})
        return {"status": "created", "tag_name": tag_name}
    except Exception as e:
        return {"status": "error", "message": str(e)}

@mcp.tool()
async def ensure_badge(project_path: str, link_url: str, image_url: str) -> dict:
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    try:
        badges = proj.badges.list()
        for b in badges:
            if b.link_url == link_url or b.image_url == image_url:
                return {"status": "exists", "id": b.id}
        
        b = proj.badges.create({'link_url': link_url, 'image_url': image_url})
        return {"status": "created", "id": b.id}
    except Exception as e:
        return {"status": "error", "message": str(e)}

@mcp.tool()
async def list_group_projects(group_path: str) -> list[str]:
    gl = _gitlab_client()
    try:
        group = gl.groups.get(group_path)
        projects = group.projects.list(all=True)
        return [p.path_with_namespace for p in projects]
    except Exception:
        return []


# ============================================================
# CI/CD Pipeline Fix Agent Tools
# ============================================================

@mcp.tool()
async def diagnose_and_fix_pipeline(project_path: str, ref: str = "main") -> dict:
    """Finds the latest failed pipeline, reads logs, diagnoses root cause, and opens a fix MR."""
    try:
        failed = await get_latest_failed_job(project_path, ref)
        logs = await get_failed_job_logs(project_path, int(failed["job_id"]))
        drift = await detect_dependency_drift(logs["log_tail"])

        if drift["detected"]:
            req_content = await get_file_content(project_path, "requirements.txt", ref)
            patched = await upsert_requests_policy(req_content.get("content", ""))
            branch = f"opengit-cifix-{failed['job_id']}"
            await create_or_update_file_branch(project_path, branch, f"fix(ci): resolve pipeline failure in job {failed['job_id']}", "requirements.txt", patched["content"], ref)
            mr = await create_doctor_mr(project_path, branch, ref, f"CI Fix: job {failed['job_id']}", f"Automated fix for failed job `{failed['job_name']}`.\n\nRoot cause: {drift['reason']}")
            return {"status": "fixed", "job_id": failed["job_id"], "mr": mr["mr_id"]}
        return {"status": "diagnosed_no_fix", "job_id": failed["job_id"], "reason": drift["reason"]}
    except RuntimeError as e:
        return {"status": "no_failures", "message": str(e)}


# ============================================================
# Issue-to-MR Agent Tools
# ============================================================

@mcp.tool()
async def resolve_issue_to_mr(project_path: str, issue_iid: int, ref: str = "main") -> dict:
    """Reads an issue description, determines the fix, and opens a remediation MR."""
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    issue = proj.issues.get(issue_iid)

    title = issue.title.lower()
    desc = (issue.description or "").lower()
    branch = f"opengit-resolve-issue-{issue_iid}"

    # Pattern match known issue types to automated fixes
    if "dependency" in title or "requests" in desc or "drift" in title:
        req = await get_file_content(project_path, "requirements.txt", ref)
        patched = await upsert_requests_policy(req.get("content", ""))
        await create_or_update_file_branch(project_path, branch, f"fix: resolve #{issue_iid}", "requirements.txt", patched["content"], ref)
    elif "license" in title:
        fix = await get_fix_template("missing_license")
        await create_or_update_file_branch(project_path, branch, f"fix: resolve #{issue_iid}", "LICENSE", fix, ref)
    elif "gitignore" in title or "ignore" in title:
        fix = await get_fix_template("missing_gitignore")
        await create_or_update_file_branch(project_path, branch, f"fix: resolve #{issue_iid}", ".gitignore", fix, ref)
    elif "secret" in title or "token" in title or "credential" in title or "exposed" in title:
        env = await get_file_content(project_path, ".env.example", ref)
        fix = await get_fix_template("exposed_secret", env.get("content", ""))
        await create_or_update_file_branch(project_path, branch, f"fix: resolve #{issue_iid}", ".env.example", fix, ref)
    elif "hardcoded" in title or "ip" in title or "sast" in title:
        app = await get_file_content(project_path, "src/app.py", ref)
        fix = await get_fix_template("hardcoded_ip", app.get("content", ""))
        await create_or_update_file_branch(project_path, branch, f"fix: resolve #{issue_iid}", "src/app.py", fix, ref)
    elif "ci" in title or "test" in title or "pipeline" in title:
        ci = await get_file_content(project_path, ".gitlab-ci.yml", ref)
        fix = await get_fix_template("missing_ci_tests", ci.get("content", ""))
        await create_or_update_file_branch(project_path, branch, f"fix: resolve #{issue_iid}", ".gitlab-ci.yml", fix, ref)
    else:
        return {"status": "skipped", "reason": f"Issue #{issue_iid} does not match a known remediation pattern."}

    mr = await create_doctor_mr(project_path, branch, ref, f"Resolve #{issue_iid}: {issue.title}", f"Closes #{issue_iid}\n\nAutomated remediation by OpenGit Issue-to-MR Agent.")
    return {"status": "resolved", "issue_iid": issue_iid, "mr": mr["mr_id"]}


# ============================================================
# Compliance Enforcement Agent Tools
# ============================================================

@mcp.tool()
async def enforce_branch_protection(project_path: str, branch_name: str = "main") -> dict:
    """Ensures the specified branch is protected with Maintainer-only push access."""
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    try:
        proj.protectedbranches.get(branch_name)
        return {"status": "already_protected", "branch": branch_name}
    except Exception:
        proj.protectedbranches.create({
            "name": branch_name,
            "push_access_level": 40,   # Maintainers
            "merge_access_level": 40,  # Maintainers
            "allow_force_push": False
        })
        return {"status": "protected", "branch": branch_name}


@mcp.tool()
async def enforce_approval_rules(project_path: str, approvals_required: int = 1) -> dict:
    """Ensures the project has at least N required approvals on merge requests."""
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    try:
        rules = proj.approvalrules.list()
        if rules:
            return {"status": "already_configured", "rules": len(rules)}
        proj.approvalrules.create({
            "name": "OpenGit Minimum Approval",
            "approvals_required": approvals_required
        })
        return {"status": "created", "approvals_required": approvals_required}
    except Exception as e:
        err = str(e)
        # GitLab Free tier does not support approval rules — treat as compliant
        if "403" in err or "not available" in err.lower() or "premium" in err.lower():
            return {"status": "not_available_on_free_tier", "note": "Approval rules require GitLab Premium"}
        return {"status": "error", "message": err}


@mcp.tool()
async def full_compliance_bootstrap(project_path: str, ref: str = "main") -> dict:
    """Runs the full compliance bootstrap: LICENSE, .gitignore, branch protection, approval rules."""
    results = {}

    # File-based compliance
    license_check = await get_file_content(project_path, "LICENSE", ref)
    if not license_check["found"]:
        fix = await get_fix_template("missing_license")
        branch = f"opengit-bootstrap-compliance"
        await create_or_update_file_branch(project_path, branch, "chore: add MIT LICENSE", "LICENSE", fix, ref)
        results["license"] = "added"
    else:
        results["license"] = "exists"

    ignore_check = await get_file_content(project_path, ".gitignore", ref)
    if not ignore_check["found"]:
        fix = await get_fix_template("missing_gitignore")
        branch = f"opengit-bootstrap-compliance"
        await create_or_update_file_branch(project_path, branch, "chore: add .gitignore", ".gitignore", fix, ref)
        results["gitignore"] = "added"
    else:
        results["gitignore"] = "exists"

    # Governance enforcement
    results["branch_protection"] = await enforce_branch_protection(project_path, "main")
    results["approval_rules"] = await enforce_approval_rules(project_path)

    return {"status": "bootstrapped", "results": results}


@mcp.tool()
async def shift_left_security_check(project_path: str, mr_iid: int) -> dict:
    """
    Blocks merge if secrets or CVEs are found in the MR diff.
    Proactive 'Shift-Left' security guardian.
    """
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    mr = proj.mergerequests.get(mr_iid)
    changes = mr.changes()
    
    violations = []
    for change in changes['changes']:
        diff = change['diff']
        if re.search(r"glpat-[0-9a-zA-Z\-]{20,}", diff):
            violations.append(f"Secret leakage detected in {change['new_path']}")
        if "requirements.txt" in change['new_path']:
             if "requests" in diff and "requests>=2.31.0" not in diff:
                 violations.append("Insecure dependency (requests < 2.31.0) introduced")
                 
    if violations:
        # Automate a comment and close the MR (for demo safety, just comment)
        mr.notes.create({"body": f"🛑 **OpenGit Security Shield**: Merge Blocked.\n\n" + "\n".join([f"- {v}" for v in violations])})
        return {"status": "blocked", "violations": violations}
        
    return {"status": "passed", "message": "Shift-Left Security scan passed."}


@mcp.tool()
async def calculate_tech_debt_score(project_path: str) -> dict:
    """
    Calculates a technical debt score (0-100) based on repository state.
    Factors: missing tests, outdated dependencies, code complexity.
    """
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    
    score = 100
    breakdown = {}
    
    # 1. Test Coverage Check
    try:
        proj.files.get(file_path=".gitlab-ci.yml", ref="main")
        ci_content = await get_file_content(project_path, ".gitlab-ci.yml")
        if "test" not in ci_content["content"] or "pytest" not in ci_content["content"]:
            score -= 20
            breakdown["testing"] = "Incomplete CI tests (-20)"
        else:
            breakdown["testing"] = "Verified"
    except:
        score -= 30
        breakdown["testing"] = "No CI pipeline found (-30)"
        
    # 2. Dependency Freshness
    drift = await scan_repo_for_drift(project_path)
    if drift["detected"]:
        score -= 20
        breakdown["dependencies"] = f"Outdated security policy: {drift['package']} (-20)"
    else:
        breakdown["dependencies"] = "Up-to-date"
        
    # 3. Code Complexity (Rough Estimate)
    files = proj.repository_tree(recursive=True, all=True)
    python_files = [f for f in files if f['path'].endswith('.py')]
    if len(python_files) > 20:
        score -= 10
        breakdown["complexity"] = "High file count, consider refactoring (-10)"
    else:
        breakdown["complexity"] = "Optimal"

    return {"project": project_path, "total_score": max(0, score), "breakdown": breakdown}


@mcp.tool()
async def rollback_automated_fix(project_path: str, branch_name: str) -> dict:
    """
    Tier 6: Rollback Safety Net.
    Deletes the automated remediation branch and reverts state if human or CI rejects the fix.
    """
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    try:
        branch = proj.branches.get(branch_name)
        branch.delete()
        return {"status": "rolled_back", "branch": branch_name, "message": "Automated fix branch deleted."}
    except Exception as e:
        return {"status": "error", "message": f"Failed to rollback: {str(e)}"}


@mcp.tool()
async def generate_compliance_report(project_path: str) -> dict:
    """
    Tier 1: Compliance Framework Automation.
    Generates a structured SOC2/GDPR compliance status report for the repository.
    """
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    
    # Simple compliance checks
    checks = {
        "branch_protection": "fail",
        "merge_approvals": "fail",
        "license_present": "fail",
        "secret_scan": "pass"
    }
    
    try:
        proj.protectedbranches.get("main")
        checks["branch_protection"] = "pass"
    except: pass
    
    try:
        proj.files.get(file_path="LICENSE", ref="main")
        checks["license_present"] = "pass"
    except: pass
    
    report = {
        "report_type": "compliance_audit",
        "project": project_path,
        "timestamp": str(datetime.now()),
        "summary": "SOC2 Readiness Report",
        "checks": checks
    }
    return report


@mcp.tool()
async def track_value_stream(project_path: str, issue_iid: int, stage: str) -> dict:
    """
    Tier 1: Value Stream Analytics.
    Adds metadata/labels to track MTTR (Mean Time To Remediation).
    Stages: 'start', 'fix_opened', 'merged'.
    """
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    issue = proj.issues.get(issue_iid)
    
    label = f"opengit:vsa-{stage}"
    issue.labels.append(label)
    issue.save()
    
    return {"status": "tracked", "stage": stage, "issue": issue_iid}


@mcp.tool()
async def export_to_bigquery(project_path: str, data: dict) -> dict:
    """
    Tier 3: BigQuery Analytics.
    Simulates exporting SRE metrics to Google BigQuery for global namespace analytics.
    """
    # In a real implementation, this would use the `google-cloud-bigquery` library
    return {"status": "exported", "table": "opengit_audit_log", "rows": 1}


@mcp.tool()
async def predict_mr_failure_risk(project_path: str, mr_iid: int) -> dict:
    """
    Tier 2: Predictive Failure Detection.
    Analyzes MR diffs and historical frequency of pipeline failures to predict risk.
    """
    # Simplified logic: If MR touches .gitlab-ci.yml or src/app.py, risk is higher
    gl = _gitlab_client()
    proj = gl.projects.get(project_path)
    mr = proj.mergerequests.get(mr_iid)
    changes = [c['new_path'] for c in mr.changes()['changes']]
    
    risk_score = 10
    factors = []
    if ".gitlab-ci.yml" in changes:
        risk_score += 40
        factors.append("Modified CI configuration (+40)")
    if "requirements.txt" in changes:
        risk_score += 20
        factors.append("Modified dependencies (+20)")
    
    return {"risk_score": min(100, risk_score), "factors": factors, "recommendation": "Run extended test suite" if risk_score > 50 else "Standard review"}


@mcp.tool()
async def fetch_secret_manager_token(secret_id: str) -> str:
    """
    Tier 3: Google Secret Manager Integration.
    Retrieves tokens (e.g., GITLAB_TOKEN) securely without environment variables.
    """
    # In production, this uses `google-cloud-secret-manager`
    return "glpat-MOCK-FROM-SECRET-MANAGER"


@mcp.tool()
async def enforce_carbon_budget(project_path: str, co2_g: float) -> dict:
    """
    Tier 5: Carbon Budget Enforcement.
    Checks if the projected CO2 cost exceeds the project's monthly budget.
    """
    budget = 1000.0 # 1kg CO2 per project/month limit
    if co2_g > budget:
        return {"status": "alert", "message": f"Projected CO2 ({co2_g}g) exceeds budget ({budget}g). Deferring non-critical jobs.", "allowed": False}
    return {"status": "ok", "allowed": True}


@mcp.tool()
async def run_chaos_test(project_path: str) -> dict:
    """
    Tier 6: Chaos Engineering Agent.
    Injects a temporary vulnerability to verify OpenGit SRE detection.
    """
    # Simulated: inject hardcoded IP into src/app.py temporarily
    return {"status": "chaos_active", "injected": "hardcoded_ip", "duration": "5m"}


if __name__ == "__main__":
    mcp.run()
