# Copyright (c) 2026 MergeGuide, Inc. All rights reserved.
# SPDX-License-Identifier: Proprietary
"""
GRC Integration Service

Orchestrates evidence synchronization between MergeGuide and GRC platforms.
Provides a unified interface for managing multiple GRC integrations.
"""

import base64
import json
import uuid
from datetime import datetime
from typing import Any

from .base import (
    EvidenceType,
    GRCAdapter,
    GRCAPIError,
    GRCAuthError,
    GRCConfig,
    GRCControlMapping,
    GRCEvidence,
    GRCPlatform,
    GRCSyncResult,
    SyncStatus,
)
from .drata import DrataAdapter
from .hyperproof import HyperproofAdapter
from .vanta import VantaAdapter


class GRCIntegrationService:
    """
    Service for managing GRC platform integrations.

    Provides:
    - Multi-platform support (Drata, Vanta, Hyperproof)
    - Evidence transformation and upload
    - Control mapping management
    - Sync orchestration
    """

    def __init__(self):
        self._adapters: dict[str, GRCAdapter] = {}
        self._configs: dict[str, GRCConfig] = {}

    def register_integration(self, org_id: str, config: GRCConfig) -> None:
        """
        Register a GRC integration for an organization.

        Args:
            org_id: Organization identifier
            config: GRC platform configuration
        """
        key = f"{org_id}:{config.platform.value}"
        self._configs[key] = config

        # Create appropriate adapter
        adapter = self._create_adapter(config)
        self._adapters[key] = adapter

    def _create_adapter(self, config: GRCConfig) -> GRCAdapter:
        """Create the appropriate adapter for the platform."""
        if config.platform == GRCPlatform.DRATA:
            return DrataAdapter(config)
        elif config.platform == GRCPlatform.VANTA:
            return VantaAdapter(config)
        elif config.platform == GRCPlatform.HYPERPROOF:
            return HyperproofAdapter(config)
        else:
            raise ValueError(f"Unsupported platform: {config.platform}")

    def get_adapter(self, org_id: str, platform: GRCPlatform) -> GRCAdapter | None:
        """Get the adapter for a specific org and platform."""
        key = f"{org_id}:{platform.value}"
        return self._adapters.get(key)

    def get_config(self, org_id: str, platform: GRCPlatform) -> GRCConfig | None:
        """Get the configuration for a specific org and platform."""
        key = f"{org_id}:{platform.value}"
        return self._configs.get(key)

    def list_integrations(self, org_id: str) -> list[dict]:
        """List all GRC integrations for an organization."""
        integrations = []
        for key, config in self._configs.items():
            if key.startswith(f"{org_id}:"):
                integrations.append(
                    {
                        "platform": config.platform.value,
                        "enabled": config.enabled,
                        "auto_sync": config.auto_sync,
                        "sync_interval_hours": config.sync_interval_hours,
                        "control_mapping_count": len(config.control_mappings),
                    }
                )
        return integrations

    async def sync_evaluation(
        self,
        org_id: str,
        platform: GRCPlatform,
        evaluation: dict[str, Any],
        control_mappings: list[GRCControlMapping] | None = None,
    ) -> GRCSyncResult:
        """
        Sync an evaluation result to a GRC platform.

        Args:
            org_id: Organization identifier
            platform: Target GRC platform
            evaluation: MergeGuide evaluation result
            control_mappings: Optional control mappings to use

        Returns:
            GRCSyncResult with sync status and details
        """
        sync_id = str(uuid.uuid4())
        started_at = datetime.utcnow()

        result = GRCSyncResult(
            sync_id=sync_id,
            platform=platform,
            organization_id=org_id,
            status=SyncStatus.IN_PROGRESS,
            started_at=started_at,
        )

        adapter = self.get_adapter(org_id, platform)
        if not adapter:
            result.status = SyncStatus.FAILED
            result.errors.append(f"No integration configured for {platform.value}")
            result.completed_at = datetime.utcnow()
            return result

        config = self.get_config(org_id, platform)
        if not config or not config.enabled:
            result.status = SyncStatus.FAILED
            result.errors.append("Integration is disabled")
            result.completed_at = datetime.utcnow()
            return result

        try:
            # Transform evaluation to evidence
            evidence = self._transform_evaluation_to_evidence(evaluation)

            # Determine control mappings
            mappings = control_mappings or self._get_default_mappings(
                config, evaluation.get("policy_id")
            )

            # Upload evidence to each mapped control
            for mapping in mappings:
                if not mapping.enabled:
                    continue

                try:
                    upload_result = await adapter.upload_evidence(evidence, mapping.grc_control_id)
                    result.evidence_uploaded += 1
                    result.details[mapping.grc_control_id] = upload_result
                except (GRCAPIError, GRCAuthError, OSError) as e:
                    result.evidence_failed += 1
                    result.errors.append(
                        f"Failed to upload to control {mapping.grc_control_id}: {str(e)}"
                    )

            # Determine final status
            if result.evidence_failed == 0 and result.evidence_uploaded > 0:
                result.status = SyncStatus.SUCCESS
            elif result.evidence_uploaded > 0:
                result.status = SyncStatus.PARTIAL
            else:
                result.status = SyncStatus.FAILED

        except (GRCAPIError, GRCAuthError, ValueError, OSError) as e:
            result.status = SyncStatus.FAILED
            result.errors.append(f"Sync failed: {str(e)}")

        result.completed_at = datetime.utcnow()
        return result

    async def sync_bulk_evaluations(
        self,
        org_id: str,
        platform: GRCPlatform,
        evaluations: list[dict[str, Any]],
    ) -> GRCSyncResult:
        """
        Sync multiple evaluations to a GRC platform.

        Args:
            org_id: Organization identifier
            platform: Target GRC platform
            evaluations: List of MergeGuide evaluation results

        Returns:
            GRCSyncResult with aggregated sync status
        """
        sync_id = str(uuid.uuid4())
        started_at = datetime.utcnow()

        result = GRCSyncResult(
            sync_id=sync_id,
            platform=platform,
            organization_id=org_id,
            status=SyncStatus.IN_PROGRESS,
            started_at=started_at,
        )

        for evaluation in evaluations:
            try:
                eval_result = await self.sync_evaluation(org_id, platform, evaluation)
                result.evidence_uploaded += eval_result.evidence_uploaded
                result.evidence_failed += eval_result.evidence_failed
                result.errors.extend(eval_result.errors)
            except (GRCAPIError, GRCAuthError, ValueError, OSError) as e:
                result.errors.append(f"Failed to sync evaluation: {str(e)}")
                result.evidence_failed += 1

        # Determine final status
        if result.evidence_failed == 0 and result.evidence_uploaded > 0:
            result.status = SyncStatus.SUCCESS
        elif result.evidence_uploaded > 0:
            result.status = SyncStatus.PARTIAL
        else:
            result.status = SyncStatus.FAILED

        result.completed_at = datetime.utcnow()
        return result

    async def list_platform_controls(
        self,
        org_id: str,
        platform: GRCPlatform,
        framework: str | None = None,
    ) -> list[dict]:
        """
        List controls from a GRC platform.

        Args:
            org_id: Organization identifier
            platform: GRC platform
            framework: Optional framework filter

        Returns:
            List of control definitions
        """
        adapter = self.get_adapter(org_id, platform)
        if not adapter:
            raise ValueError(f"No integration configured for {platform.value}")

        return await adapter.list_controls(framework)

    async def test_connection(self, org_id: str, platform: GRCPlatform) -> dict[str, Any]:
        """
        Test the connection to a GRC platform.

        Returns:
            Connection test result with status and details
        """
        adapter = self.get_adapter(org_id, platform)
        if not adapter:
            return {
                "success": False,
                "error": f"No integration configured for {platform.value}",
            }

        try:
            # Try to authenticate and list controls
            await adapter.ensure_authenticated()
            controls = await adapter.list_controls()

            return {
                "success": True,
                "platform": platform.value,
                "control_count": len(controls),
                "message": f"Successfully connected to {platform.value}",
            }
        except Exception as e:
            return {
                "success": False,
                "platform": platform.value,
                "error": str(e),
            }

    def _transform_evaluation_to_evidence(self, evaluation: dict[str, Any]) -> GRCEvidence:
        """
        Transform a MergeGuide evaluation to GRC evidence.
        """
        # Create JSON content
        evidence_content = {
            "evaluation_id": evaluation.get("evaluation_id"),
            "repository": evaluation.get("repository"),
            "ref": evaluation.get("ref"),
            "timestamp": evaluation.get("timestamp"),
            "policy_results": evaluation.get("policy_results", []),
            "summary": {
                "total_policies": evaluation.get("total_policies", 0),
                "passed_policies": evaluation.get("passed_policies", 0),
                "failed_policies": evaluation.get("failed_policies", 0),
                "violation_count": evaluation.get("violation_count", 0),
            },
            "metadata": evaluation.get("metadata", {}),
        }

        # Base64 encode the content
        content_bytes = json.dumps(evidence_content, indent=2).encode("utf-8")
        content_b64 = base64.b64encode(content_bytes).decode("utf-8")

        # Determine pass/fail
        passed = evaluation.get("passed", evaluation.get("failed_policies", 1) == 0)

        return GRCEvidence(
            evidence_id=str(uuid.uuid4()),
            evidence_type=EvidenceType.FILE,
            title=f"MergeGuide Evaluation: {evaluation.get('repository', 'Unknown')}",
            description=(
                f"Policy evaluation for {evaluation.get('repository')} "
                f"at {evaluation.get('ref', 'HEAD')}. "
                f"Result: {'PASS' if passed else 'FAIL'}"
            ),
            content=content_b64,
            file_name=f"mergeguide-evaluation-{evaluation.get('evaluation_id', 'unknown')}.json",
            mime_type="application/json",
            control_ids=[],
            evaluation_id=evaluation.get("evaluation_id"),
            repository=evaluation.get("repository"),
            policy_id=evaluation.get("policy_id"),
            policy_name=evaluation.get("policy_name"),
            passed=passed,
            violation_count=evaluation.get("violation_count", 0),
            metadata=evaluation.get("metadata", {}),
        )

    def _get_default_mappings(
        self, config: GRCConfig, policy_id: str | None
    ) -> list[GRCControlMapping]:
        """
        Get default control mappings for a policy.

        Uses the config's control_mappings dictionary.
        """
        mappings = []

        if not policy_id or not config.control_mappings:
            return mappings

        control_id = config.control_mappings.get(policy_id)
        if control_id:
            mappings.append(
                GRCControlMapping(
                    mapping_id=str(uuid.uuid4()),
                    platform=config.platform,
                    mergeguide_policy_id=policy_id,
                    mergeguide_policy_name=policy_id,  # Would need lookup
                    grc_control_id=control_id,
                    grc_control_name="",  # Would need lookup
                    framework="",
                    framework_control="",
                    enabled=True,
                )
            )

        return mappings

    async def close(self):
        """Close all adapters."""
        for adapter in self._adapters.values():
            await adapter.close()


# Default framework control mappings
DEFAULT_SOC2_MAPPINGS = {
    # Policy ID -> SOC 2 control reference
    "secrets-detection": "CC6.1",
    "dangerous-functions": "CC8.1",
    "debug-statements": "CC8.1",
    "todo-fixme": "CC8.1",
    "xss-prevention": "CC6.1",
    "sql-injection": "CC6.1",
    "input-validation": "CC6.1",
    "error-handling": "CC7.1",
}

DEFAULT_ISO27001_MAPPINGS = {
    # Policy ID -> ISO 27001:2022 Annex A control
    "secrets-detection": "8.25",
    "dangerous-functions": "8.28",
    "debug-statements": "8.28",
    "todo-fixme": "8.28",
    "xss-prevention": "8.28",
    "sql-injection": "8.28",
    "input-validation": "8.28",
    "error-handling": "8.29",
}
