# Copyright (c) 2026 MergeGuide, Inc. All rights reserved.
# SPDX-License-Identifier: Proprietary
"""Fire-and-forget upload of hook findings to MergeGuide API.

Uploads pre-commit/pre-push findings to the MergeGuide dashboard for
audit trail purposes. Uses stdlib only (urllib.request) to avoid
adding dependencies. All uploads happen on daemon threads so they
never block the git hook process.

SOC 2 Relevance:
    - CC3.2: Risk assessment evidence from local scanning
    - CC4.1: Continuous monitoring via shift-left findings
"""

from __future__ import annotations

import json
import os
import threading
import urllib.error
import urllib.request
from pathlib import Path
from typing import Any


def upload_hook_findings_async(
    violations: list[Any],
    files_checked: list[str],
    hook_type: str,
    commit_sha: str,
    repo: str,
    author: str,
) -> None:
    """Spawn a daemon thread to POST findings to the MergeGuide API.

    Non-blocking. Silent no-op if API credentials not configured.
    Hook exit time is never affected by upload success or failure.

    Args:
        violations: List of Violation objects from the evaluation.
            Each must have rule_id, severity, file_path, line_no, message.
        files_checked: List of file paths that were checked.
        hook_type: Hook type (e.g., "pre-commit", "pre-push", "bypass").
        commit_sha: Current commit SHA (or "unknown").
        repo: Repository remote URL (or "unknown").
        author: Git user email (or "unknown").
    """
    api_key = _resolve_api_key()
    api_url = _resolve_api_url()

    if not api_key or not api_url:
        return  # Silent no-op -- credentials not configured

    thread = threading.Thread(
        target=_upload_findings,
        args=(violations, files_checked, hook_type, commit_sha, repo, author, api_key, api_url),
        daemon=True,  # CRITICAL: daemon=True so thread doesn't block process exit
    )
    thread.start()
    # Do NOT join -- fire-and-forget


def _upload_findings(
    violations: list[Any],
    files_checked: list[str],
    hook_type: str,
    commit_sha: str,
    repo: str,
    author: str,
    api_key: str,
    api_url: str,
) -> None:
    """Worker: POST findings payload. All exceptions silently swallowed."""
    try:
        payload = {
            "source": hook_type,
            "commit_sha": commit_sha,
            "repo": repo,
            "author": author,
            "files_checked": len(files_checked),
            "violations": [
                {
                    "rule_id": getattr(v, "rule_id", "unknown"),
                    "severity": getattr(v, "severity", "unknown"),
                    "file_path": getattr(v, "file_path", "unknown"),
                    "line_no": getattr(v, "line_no", 0),
                    "message": getattr(v, "message", ""),
                }
                for v in violations
            ],
        }
        data = json.dumps(payload).encode("utf-8")
        req = urllib.request.Request(  # noqa: S310
            f"{api_url}/api/internal/evaluations",
            data=data,
            headers={
                "Content-Type": "application/json",
                "X-MergeGuide-API-Key": api_key,
            },
            method="POST",
        )
        # 5-second timeout -- never block
        urllib.request.urlopen(req, timeout=5)  # noqa: S310
    except Exception:
        pass  # Always silent -- hook must never fail due to upload


def _resolve_api_key() -> str | None:
    """Resolve API key from env var, then ~/.mergeguide/credentials, then .mergeguide/config.yaml.

    Returns:
        API key string if found, None otherwise.
    """
    # 1. Environment variable (most common in CI/production)
    key = os.environ.get("MERGEGUIDE_API_KEY", "")
    if key:
        return key

    # 2. ~/.mergeguide/credentials (user-level config)
    creds_path = Path.home() / ".mergeguide" / "credentials"
    if creds_path.exists():
        try:
            for line in creds_path.read_text().splitlines():
                line = line.strip()
                if line.startswith("api_key"):
                    # Support both "api_key=VALUE" and "api_key: VALUE" formats
                    for sep in ("=", ":"):
                        if sep in line:
                            val = line.split(sep, 1)[1].strip().strip("'\"")
                            if val:
                                return val
        except Exception:
            pass

    # 3. .mergeguide/config.yaml (repo-local)
    local_config = Path(".mergeguide/config.yaml")
    if local_config.exists():
        try:
            # Parse YAML without importing yaml (stdlib only)
            for line in local_config.read_text().splitlines():
                line = line.strip()
                if line.startswith("api_key"):
                    for sep in (":", "="):
                        if sep in line:
                            val = line.split(sep, 1)[1].strip().strip("'\"")
                            if val:
                                return val
        except Exception:
            pass

    return None


def _resolve_api_url() -> str:
    """Resolve API URL from env var or default.

    Returns:
        The API base URL string.
    """
    return os.environ.get("MERGEGUIDE_API_URL", "https://portal.mergeguide.ai")
