"""SolutionBuilder — main SDK entry point.

Provides ``run()``, ``compile()``, ``design()``, and ``pipeline()``
methods that dispatch to the appropriate workflow via the registry.
"""

from __future__ import annotations

from typing import Any, Dict, List, Optional

from databridge_core.sdk.core.config import SDKConfig
from databridge_core.sdk.core.context import SDKContext
from databridge_core.sdk.core.types import CompiledArtifact, Result, WorkflowPlan
from databridge_core.sdk.workflows.registry import WorkflowRegistry
from databridge_core.sdk.workflows.result import WorkflowResult


class SolutionBuilder:
    """Main entry point for the DataBridge SDK.

    Usage::

        builder = SolutionBuilder()

        # List available workflows
        builder.list_workflows()

        # Run a workflow
        result = builder.run("reconciliation.hash_compare", {
            "source_a_path": "gl.csv",
            "source_b_path": "tb.csv",
            "key_columns": "account_id",
        })

        # Compile to Snowflake
        artifacts = builder.compile("hierarchy_reporting.wright_pipeline", {
            "project_id": "proj_123",
        })

        # LLM-assisted design (requires enable_agent_assist=True)
        plan = builder.design("reconcile GL against TB for Q4 close")

        # Multi-step pipeline
        result = builder.pipeline([
            ("data_cleaning.profiling", {"source_path": "data.csv"}),
            ("reconciliation.hash_compare", {"source_a_path": "a.csv", ...}),
        ])
    """

    # Tier ordering for license checks
    _TIER_RANK = {"CE": 0, "PRO": 1, "ENTERPRISE": 2}

    def __init__(
        self,
        config: Optional[SDKConfig] = None,
        user_id: Optional[str] = None,
    ):
        self._ctx = SDKContext(config=config, user_id=user_id)
        self._registry = WorkflowRegistry()
        self._registry.discover()
        self._license_tier = self._validate_license()

    def _validate_license(self) -> str:
        """Validate license and return tier (CE/PRO/ENTERPRISE)."""
        try:
            from src.licensing.client import LicenseClient
            import os

            key = os.environ.get("DATABRIDGE_LICENSE_KEY", "")
            if not key:
                return "CE"
            client = LicenseClient()
            result = client.validate(key)
            return result.get("tier", "CE") if result.get("valid") else "CE"
        except (ImportError, Exception):
            return "CE"  # Graceful fallback when licensing module unavailable

    def _tier_allowed(self, required: str, current: str) -> bool:
        """Check if current tier meets the required tier."""
        return self._TIER_RANK.get(current, 0) >= self._TIER_RANK.get(required, 0)

    @property
    def context(self) -> SDKContext:
        return self._ctx

    def list_workflows(self) -> List[Dict[str, str]]:
        """List all available workflows with metadata."""
        return self._registry.list_all()

    def list_categories(self) -> List[Dict[str, Any]]:
        """List workflow categories with counts."""
        return self._registry.list_categories()

    def search_workflows(self, query: str) -> List[Dict[str, str]]:
        """Search workflows by keyword."""
        return self._registry.search(query)

    def run(
        self,
        workflow_name: str,
        params: Optional[Dict[str, Any]] = None,
    ) -> Result:
        """Execute a workflow by name.

        Args:
            workflow_name: Dotted workflow name (e.g. ``reconciliation.hash_compare``).
            params: Parameters for the workflow.

        Returns:
            Result with step outcomes.

        Raises:
            sdk.core.exceptions.WorkflowNotFoundError: If workflow not found.
            sdk.core.exceptions.ValidationError: If params invalid.
        """
        from databridge_core.sdk.core.exceptions import LicenseError, WorkflowNotFoundError

        wf_cls = self._registry.get(workflow_name)
        if not wf_cls:
            raise WorkflowNotFoundError(
                f"Workflow '{workflow_name}' not found. "
                f"Use list_workflows() to see available workflows."
            )

        wf = wf_cls()
        params = params or {}

        # Enforce license tier requirement
        required_tier = getattr(wf, "required_tier", "CE")
        if not self._tier_allowed(required_tier, self._license_tier):
            raise LicenseError(
                f"Workflow '{workflow_name}' requires {required_tier} license. "
                f"Current tier: {self._license_tier}"
            )

        self._ctx.audit.log_workflow_start(workflow_name, params)
        validated = wf.validate(params)
        result = wf.execute(validated, self._ctx)
        self._ctx.audit.log_workflow_end(
            workflow_name,
            result_id=result.result_id,
            status=result.status.value,
        )

        return result

    def compile(
        self,
        workflow_name: str,
        params: Optional[Dict[str, Any]] = None,
    ) -> List[CompiledArtifact]:
        """Compile a workflow into Snowflake native objects.

        Args:
            workflow_name: Dotted workflow name.
            params: Parameters for the workflow.

        Returns:
            List of CompiledArtifact with DDL statements.

        Raises:
            sdk.core.exceptions.WorkflowNotFoundError: If workflow not found.
            sdk.core.exceptions.CompilationError: On compilation failure.
        """
        from databridge_core.sdk.core.exceptions import WorkflowNotFoundError

        wf_cls = self._registry.get(workflow_name)
        if not wf_cls:
            raise WorkflowNotFoundError(
                f"Workflow '{workflow_name}' not found."
            )

        wf = wf_cls()
        params = params or {}
        validated = wf.validate(params)

        self._ctx.audit.log(
            action="compile_start",
            workflow=workflow_name,
            details={"params": params},
        )

        artifacts = wf.compile_to_snowflake(validated, self._ctx)

        self._ctx.audit.log(
            action="compile_end",
            workflow=workflow_name,
            details={"artifact_count": len(artifacts)},
        )

        return artifacts

    def design(
        self,
        user_request: str,
        constraints: Optional[Dict[str, Any]] = None,
    ) -> WorkflowPlan:
        """Use LLM to design a workflow plan for a natural-language request.

        Requires ``enable_agent_assist=True`` in SDKConfig.

        Reuse: ``sdk.agents.assistant.SDKAssistant.design()``

        Args:
            user_request: Natural-language description of what to do.
            constraints: Optional constraints (budget, time, target DB).

        Returns:
            WorkflowPlan with recommended workflow and parameters.

        Raises:
            sdk.core.exceptions.SDKError: If agent assist is disabled.
        """
        from databridge_core.sdk.core.exceptions import SDKError

        if not self._ctx.config.enable_agent_assist:
            raise SDKError(
                "Agent assist is disabled. Set SDK_ENABLE_AGENT_ASSIST=true."
            )

        from databridge_core.sdk.agents.assistant import SDKAssistant

        assistant = SDKAssistant(ctx=self._ctx, registry=self._registry)
        return assistant.design(user_request, constraints)

    def pipeline(
        self,
        steps: List[tuple],
    ) -> List[Result]:
        """Execute a sequence of workflows as a pipeline.

        Args:
            steps: List of ``(workflow_name, params)`` tuples.

        Returns:
            List of Results, one per workflow step.
        """
        results = []
        for workflow_name, params in steps:
            result = self.run(workflow_name, params or {})
            results.append(result)
        return results

    def graph_health(self) -> Dict[str, Any]:
        """Check health of all graph subsystems.

        Returns structured report with per-system availability and latency.
        """
        from databridge_core.sdk.core.graph_probe import GraphProbe

        return GraphProbe(self._ctx).check()

    # --- Convenience Domain Wrappers (Invisible Architect) ---

    def hunt_synergies(
        self, 
        cluster_id: str, 
        company_files: List[Dict[str, str]], 
        efficiency_gain: float = 0.25
    ) -> Result:
        """Convenience wrapper for the M&A Synergy Hunter workflow."""
        return self.run("mna.synergy_hunter", {
            "cluster_id": cluster_id,
            "company_files": company_files,
            "efficiency_gain": efficiency_gain
        })

    def audit_forensics(
        self,
        mode: str,
        **kwargs
    ) -> Result:
        """
        Convenience wrapper for the Forensics Audit workflow.
        
        Args:
            mode: 'standard' or 'advanced'
            **kwargs: mapping_file, data_file, summary_file (for standard)
                      coa_file, inv_file, ledger_file, model_file (for advanced)
        """
        params = {"audit_mode": mode}
        params.update(kwargs)
        return self.run("forensics.audit", params)

    def calculate_ais(
        self,
        forensic_results: Dict[str, Any],
        design_violations: List[Any],
        synergy_report: Optional[Dict[str, Any]] = None
    ) -> Result:
        """
        Calculates the unified Architectural Integrity Score (AIS).
        """
        return self.run("architect.ais_score", {
            "forensic_results": forensic_results,
            "design_violations": design_violations,
            "synergy_report": synergy_report
        })

    def close(self) -> None:
        """Clean up resources."""
        self._ctx.close()

    def __enter__(self) -> "SolutionBuilder":
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        self.close()
