"""
DeepSweep Threat Intelligence Signal System
==========================================

Every validation contributes anonymous threat signals to community defense,
creating a flywheel: more validations → better threat data → better protection.

Signal-Powered Capabilities (Current & Roadmap):
- Pattern frequency tracking across ecosystem
- Tool-specific threat profiles (Cursor vs Copilot vs Claude Code)
- Early warning system for novel attack patterns
- Security improvement tracking over time
- Peer benchmarking ("safer than 80% of users")

Privacy Guarantees:
- NO source code or file contents transmitted
- NO file paths or repository names
- NO PII, credentials, or secrets
- Only 8-char prefix of hashed install ID stored
- Opt out anytime with --offline flag

Architecture:
- Fire-and-forget: Never blocks CLI execution
- 2-second timeout: Network issues don't slow validation
- Silent failure: Telemetry errors never crash the CLI
- Rate limited: 100 signals/minute per install

API Contract:
- Endpoint: POST https://api.deepsweep.ai/v1/signal
- Response: 202 Accepted
- TTL: 30 days (auto-expiring signals)

Learn more: https://deepsweep.ai/docs/signal-intelligence
"""

import contextlib
import hashlib
import json
import os
import platform
import threading
import uuid
from dataclasses import asdict, dataclass, field
from datetime import datetime, timezone
from typing import Any, Final
from urllib.parse import urlparse
from urllib.request import Request, urlopen

from deepsweep.constants import VERSION

# Threat Intelligence endpoint (Essential tier)
THREAT_INTEL_ENDPOINT: Final[str] = os.environ.get(
    "DEEPSWEEP_INTEL_ENDPOINT", "https://api.deepsweep.ai/v1/signal"
)

# Request timeout (never block CLI)
REQUEST_TIMEOUT: Final[float] = 2.0

# Install ID cache
_install_id_cache: str | None = None


def _get_install_id() -> str:
    """
    Get anonymized install ID (SHA-256 hash of machine identifiers).

    Returns 64 hex characters (full SHA-256 hash) per API contract.
    Server stores only first 8 characters for privacy-preserving analytics.

    Used only for:
    - Deduplication in analytics
    - Cohort analysis (not individual tracking)
    - Security improvement tracking over time

    Cannot be reversed to identify user or machine.
    """
    global _install_id_cache

    if _install_id_cache is not None:
        return _install_id_cache

    components = [
        platform.node(),
        platform.machine(),
        platform.processor(),
        str(uuid.getnode()),
    ]

    raw = "|".join(components).encode()
    _install_id_cache = hashlib.sha256(raw).hexdigest()  # Full 64 chars per API contract
    return _install_id_cache


@dataclass
class ThreatSignal:
    """
    Anonymized threat intelligence signal from CLI validation.

    This signal contributes to community defense without exposing
    any sensitive information. Every validation helps detect emerging
    threats faster across the AI coding ecosystem.

    What this enables:
    - "This pattern is spiking across the ecosystem" (threat alerts)
    - "Your security improved 15% this month" (progress tracking)
    - "Your MCP config is safer than 80% of users" (benchmarking)
    - "Novel attack pattern detected" (zero-day early warning)

    Privacy guarantees:
    - NO source code or file contents
    - NO file paths or repository names
    - NO PII, credentials, or secrets
    - SHA-256 hashed machine IDs only (8-char prefix stored)

    Opt out: Run with --offline flag to disable signal transmission.

    Attributes:
        pattern_ids: Detection pattern IDs triggered (e.g., ["DS-PI-001"])
        cve_matches: CVE identifiers found (e.g., ["CVE-2025-43570"])
        severity_counts: Count by severity level
        tool_context: AI tools detected (cursor, copilot, mcp, etc.)
        score: Composite security score (0-100, higher is safer)
        grade: Letter grade (A-F)
        finding_count: Total security findings
        duration_ms: Validation duration in milliseconds
        cli_version: DeepSweep CLI version
        python_version: Python interpreter version
        os_type: Operating system (darwin/linux/windows)
        is_ci: Whether running in CI/CD environment
        ci_provider: CI platform if detected (github/gitlab/etc.)
        install_id: SHA-256 hash of machine identifier (privacy-preserving)
        session_id: Random per-session identifier
        timestamp: ISO 8601 UTC timestamp
    """

    # Detection results
    pattern_ids: list[str] = field(default_factory=list)
    cve_matches: list[str] = field(default_factory=list)
    severity_counts: dict[str, int] = field(default_factory=dict)

    # Validation context
    tool_context: list[str] = field(default_factory=list)
    score: int = 0
    grade: str = ""
    finding_count: int = 0
    duration_ms: int = 0

    # Client metadata
    cli_version: str = ""
    python_version: str = ""
    os_type: str = ""
    is_ci: bool = False
    ci_provider: str | None = None

    # Privacy-preserving identifiers
    install_id: str = field(default_factory=_get_install_id)
    session_id: str = field(default_factory=lambda: uuid.uuid4().hex[:16])
    timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())


def _detect_ci() -> tuple[bool, str | None]:
    """Detect if running in CI environment."""
    ci_indicators = {
        "GITHUB_ACTIONS": "github",
        "GITLAB_CI": "gitlab",
        "CIRCLECI": "circleci",
        "JENKINS_URL": "jenkins",
        "TRAVIS": "travis",
        "BUILDKITE": "buildkite",
        "AZURE_PIPELINES": "azure",
        "BITBUCKET_PIPELINES": "bitbucket",
        "CI": None,
    }

    for env_var, provider in ci_indicators.items():
        if os.environ.get(env_var):
            return True, provider

    return False, None


def _send_async(
    url: str,
    data: dict[str, Any],
    install_id: str,
    timeout: float = REQUEST_TIMEOUT,
) -> None:
    """
    Send data asynchronously (fire and forget).

    Implements the locked API contract:
    - Headers: Content-Type, User-Agent, X-Client-Version, X-Install-ID
    - Method: POST
    - Timeout: 2 seconds (never blocks CLI)
    - Silent failure: Errors are suppressed
    """
    parsed = urlparse(url)
    if parsed.scheme not in ("http", "https"):
        return  # silently drop invalid schemes

    def _do_send() -> None:
        with contextlib.suppress(Exception):
            request = Request(
                url,
                data=json.dumps(data).encode("utf-8"),
                headers={
                    "Content-Type": "application/json",
                    "User-Agent": f"deepsweep-cli/{VERSION}",
                    "X-Client-Version": VERSION,
                    "X-Install-ID": install_id,
                },
                method="POST",
            )
            with urlopen(request, timeout=timeout):  # nosec
                pass

    threading.Thread(target=_do_send, daemon=True).start()


def send_threat_signal(signal: ThreatSignal, offline_mode: bool = False) -> None:
    """
    Contribute threat signal to community intelligence.

    This function sends anonymized threat pattern data to DeepSweep's
    community intelligence API. The data helps detect emerging threats
    faster and enables features like security benchmarking and
    improvement tracking.

    Design principles:
    - Fire-and-forget: Returns immediately, sends in background thread
    - Never blocks: CLI execution is never delayed by network
    - Silent failure: Network errors are swallowed (telemetry != critical path)
    - Privacy-first: No sensitive data ever transmitted

    What happens with your signal:
    1. Anonymous pattern data joins the collective threat landscape
    2. Aggregate analysis detects emerging attack patterns
    3. Better detection rules protect all DeepSweep users
    4. Your security posture can be tracked over time (anonymously)

    Args:
        signal: ThreatSignal dataclass with validation results
        offline_mode: If True, skip transmission entirely

    Returns:
        None (fire-and-forget, no response processing)

    Example:
        >>> signal = ThreatSignal(
        ...     pattern_ids=["CURSOR-RULES-001"],
        ...     score=85,
        ...     grade="B",
        ...     # ... other fields
        ... )
        >>> send_threat_signal(signal)  # Returns immediately
        >>> # Signal sent in background, never blocks

    Opt out:
        - Pass offline_mode=True
        - Or run CLI with --offline flag
        - Or set DEEPSWEEP_OFFLINE=1 environment variable
    """
    if offline_mode:
        return

    # Send direct payload per API contract (no wrapper)
    _send_async(
        THREAT_INTEL_ENDPOINT,
        asdict(signal),
        install_id=signal.install_id,
    )


def create_threat_signal(
    findings_count: int = 0,
    score: int = 0,
    grade: str = "",
    duration_ms: int = 0,
    pattern_ids: list[str] | None = None,
    cve_matches: list[str] | None = None,
    severity_counts: dict[str, int] | None = None,
) -> ThreatSignal:
    """
    Create a threat signal from validation results.

    Constructs a ThreatSignal with all required fields per API contract.
    Automatically detects CI environment and populates client metadata.
    """
    is_ci, ci_provider = _detect_ci()

    return ThreatSignal(
        pattern_ids=pattern_ids or [],
        cve_matches=cve_matches or [],
        severity_counts=severity_counts or {},
        tool_context=[],
        score=score,
        grade=grade,
        finding_count=findings_count,
        duration_ms=duration_ms,
        cli_version=VERSION,
        python_version=platform.python_version(),
        os_type=platform.system().lower(),
        is_ci=is_ci,
        ci_provider=ci_provider,
    )
