"""
Unified MCP tools for Enterprise DW Consolidation Engine.

3 action-dispatched tools, 12 actions total:
- enterprise_dw(action, ...) — 4 actions: analyze_grain, align_facts, generate_union_sql, validate_cross_grain
- enterprise_dim(action, ...) — 4 actions: master_dimension, generate_key_map, detect_overlap, generate_dim_ddl
- enterprise_matrix(action, ...) — 4 actions: build_matrix, conformance_report, generate_all_ddl, deploy
"""
from __future__ import annotations

import json
import logging
from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)

# ============================================================================
# Lazy singletons
# ============================================================================

_grain_analyzer = None
_fact_harmonizer = None
_dim_master = None
_bus_matrix_gen = None
_cross_validator = None
_ddl_gen = None


def _ensure_grain_analyzer():
    global _grain_analyzer
    if _grain_analyzer is None:
        from .grain_analyzer import GrainAnalyzer
        _grain_analyzer = GrainAnalyzer()
    return _grain_analyzer


def _ensure_fact_harmonizer():
    global _fact_harmonizer
    if _fact_harmonizer is None:
        from .fact_harmonizer import FactHarmonizer
        _fact_harmonizer = FactHarmonizer()
    return _fact_harmonizer


def _ensure_dim_master():
    global _dim_master
    if _dim_master is None:
        from .conformed_dim_master import ConformedDimMaster
        _dim_master = ConformedDimMaster()
    return _dim_master


def _ensure_bus_matrix_gen():
    global _bus_matrix_gen
    if _bus_matrix_gen is None:
        from .enterprise_bus_matrix import EnterpriseBusMatrixGenerator
        _bus_matrix_gen = EnterpriseBusMatrixGenerator()
    return _bus_matrix_gen


def _ensure_cross_validator():
    global _cross_validator
    if _cross_validator is None:
        from .cross_grain_validator import CrossGrainValidator
        _cross_validator = CrossGrainValidator()
    return _cross_validator


def _ensure_ddl_gen(schema_prefix: str = ""):
    global _ddl_gen
    if _ddl_gen is None:
        from .ddl_generator import EnterpriseDDLGenerator
        _ddl_gen = EnterpriseDDLGenerator(schema_prefix=schema_prefix)
    return _ddl_gen


# ============================================================================
# enterprise_dw action handlers
# ============================================================================

def _edw_analyze_grain(settings, **kwargs) -> Dict[str, Any]:
    systems_json = kwargs.get("systems_json", "")
    if not systems_json:
        return {"error": "systems_json is required (JSON array of {system, tables})"}

    try:
        systems = json.loads(systems_json) if isinstance(systems_json, str) else systems_json
    except json.JSONDecodeError as e:
        return {"error": f"Invalid JSON: {e}"}

    analyzer = _ensure_grain_analyzer()
    all_specs = []
    for sys_def in systems:
        system_name = sys_def.get("system", "")
        tables = sys_def.get("tables", [])
        if not system_name:
            continue
        specs = analyzer.register_system(system_name, tables)
        all_specs.extend([s.model_dump() for s in specs])

    comparisons = analyzer.compare_all()
    return {
        "status": "success",
        "grain_specs": all_specs,
        "comparisons": [c.model_dump() for c in comparisons],
        "total_specs": len(all_specs),
        "total_comparisons": len(comparisons),
    }


def _edw_align_facts(settings, **kwargs) -> Dict[str, Any]:
    comparison_json = kwargs.get("comparison_json", "")
    columns_a_json = kwargs.get("columns_a_json", "")
    columns_b_json = kwargs.get("columns_b_json", "")

    if not all([comparison_json, columns_a_json, columns_b_json]):
        return {"error": "comparison_json, columns_a_json, columns_b_json are all required"}

    try:
        from .contracts import GrainComparison
        comp_data = json.loads(comparison_json) if isinstance(comparison_json, str) else comparison_json
        comparison = GrainComparison(**comp_data)
        cols_a = json.loads(columns_a_json) if isinstance(columns_a_json, str) else columns_a_json
        cols_b = json.loads(columns_b_json) if isinstance(columns_b_json, str) else columns_b_json
    except Exception as e:
        return {"error": f"Invalid input: {e}"}

    harmonizer = _ensure_fact_harmonizer()
    alignment = harmonizer.align_facts(comparison, cols_a, cols_b)
    return {
        "status": "success",
        "alignment": alignment.model_dump(),
    }


def _edw_generate_union_sql(settings, **kwargs) -> Dict[str, Any]:
    alignment_json = kwargs.get("alignment_json", "")
    target_table = kwargs.get("target_table", "")
    schema_prefix = kwargs.get("schema_prefix", "")

    if not alignment_json:
        return {"error": "alignment_json is required"}

    try:
        from .contracts import FactAlignment
        al_data = json.loads(alignment_json) if isinstance(alignment_json, str) else alignment_json
        alignment = FactAlignment(**al_data)
    except Exception as e:
        return {"error": f"Invalid alignment JSON: {e}"}

    harmonizer = _ensure_fact_harmonizer()
    sql = harmonizer.generate_union_sql(alignment, target_table, schema_prefix)
    return {"status": "success", "union_sql": sql}


def _edw_validate_cross_grain(settings, **kwargs) -> Dict[str, Any]:
    fact_table = kwargs.get("fact_table", "")
    fine_grain = kwargs.get("fine_grain", "daily")
    coarse_grain = kwargs.get("coarse_grain", "monthly")
    measures_json = kwargs.get("measures_json", "")

    if not fact_table or not measures_json:
        return {"error": "fact_table and measures_json are required"}

    try:
        from .contracts import GrainLevel
        fg = GrainLevel(fine_grain)
        cg = GrainLevel(coarse_grain)
        measures = json.loads(measures_json) if isinstance(measures_json, str) else measures_json
    except Exception as e:
        return {"error": f"Invalid input: {e}"}

    validator = _ensure_cross_validator()
    results = validator.validate(fact_table, fg, cg, measures)
    return validator.summary(results)


# ============================================================================
# enterprise_dim action handlers
# ============================================================================

def _edim_master_dimension(settings, **kwargs) -> Dict[str, Any]:
    dim_json = kwargs.get("dimension_json", "")
    data_json = kwargs.get("system_data_json", "")
    strategy = kwargs.get("strategy", "first_match")
    priority = kwargs.get("priority_system", "")

    if not dim_json:
        return {"error": "dimension_json is required"}

    try:
        from .contracts import ConformedDimension, MasteringStrategy
        dim_data = json.loads(dim_json) if isinstance(dim_json, str) else dim_json
        dim = ConformedDimension(**dim_data)
        system_data = json.loads(data_json) if isinstance(data_json, str) and data_json else {}
        strat = MasteringStrategy(strategy)
    except Exception as e:
        return {"error": f"Invalid input: {e}"}

    master = _ensure_dim_master()
    key_map = master.master_dimension(dim, system_data, strat, priority)
    return {
        "status": "success",
        "key_map": key_map.model_dump(),
    }


def _edim_generate_key_map(settings, **kwargs) -> Dict[str, Any]:
    return _edim_master_dimension(settings, **kwargs)


def _edim_detect_overlap(settings, **kwargs) -> Dict[str, Any]:
    systems_json = kwargs.get("systems_json", "")
    if not systems_json:
        return {"error": "systems_json is required"}

    try:
        systems = json.loads(systems_json) if isinstance(systems_json, str) else systems_json
    except Exception as e:
        return {"error": f"Invalid JSON: {e}"}

    master = _ensure_dim_master()
    for sys_def in systems:
        master.register_system_dimensions(
            sys_def.get("system", ""),
            sys_def.get("dimensions", []),
        )

    conformed = master.detect_conformed()
    return {
        "status": "success",
        "conformed_dimensions": [c.model_dump() for c in conformed],
        "total_conformed": len(conformed),
    }


def _edim_generate_dim_ddl(settings, **kwargs) -> Dict[str, Any]:
    dim_json = kwargs.get("dimension_json", "")
    schema_prefix = kwargs.get("schema_prefix", "")
    if not dim_json:
        return {"error": "dimension_json is required"}

    try:
        from .contracts import ConformedDimension
        dim_data = json.loads(dim_json) if isinstance(dim_json, str) else dim_json
        dim = ConformedDimension(**dim_data)
    except Exception as e:
        return {"error": f"Invalid input: {e}"}

    master = _ensure_dim_master()
    ddl = master.generate_dim_ddl(dim, schema_prefix)
    return {"status": "success", "ddl": ddl}


# ============================================================================
# enterprise_matrix action handlers
# ============================================================================

def _emat_build_matrix(settings, **kwargs) -> Dict[str, Any]:
    facts_json = kwargs.get("facts_json", "")
    dims_json = kwargs.get("dimensions_json", "")

    if not facts_json:
        return {"error": "facts_json is required"}

    try:
        from .contracts import GrainLevel, ConformedDimension
        facts = json.loads(facts_json) if isinstance(facts_json, str) else facts_json
        dims = json.loads(dims_json) if isinstance(dims_json, str) and dims_json else []
    except Exception as e:
        return {"error": f"Invalid JSON: {e}"}

    gen = _ensure_bus_matrix_gen()
    for f in facts:
        gen.add_fact(
            f["table_name"],
            f.get("source_systems", []),
            GrainLevel(f.get("grain", "monthly")),
            f.get("dimension_columns", []),
        )
    for d in dims:
        from .contracts import ConformedDimension
        gen.add_conformed_dimension(ConformedDimension(**d))

    matrix = gen.build()
    return {
        "status": "success",
        "matrix": matrix.model_dump(),
        "markdown": gen.to_markdown(),
    }


def _emat_conformance_report(settings, **kwargs) -> Dict[str, Any]:
    gen = _ensure_bus_matrix_gen()
    return gen.conformance_report()


def _emat_generate_all_ddl(settings, **kwargs) -> Dict[str, Any]:
    facts_json = kwargs.get("facts_json", "")
    dims_json = kwargs.get("dimensions_json", "")
    schema_prefix = kwargs.get("schema_prefix", "")

    try:
        from .contracts import FactAlignment, ConformedDimension
        facts = [FactAlignment(**f) for f in (json.loads(facts_json) if isinstance(facts_json, str) and facts_json else [])]
        dims = [ConformedDimension(**d) for d in (json.loads(dims_json) if isinstance(dims_json, str) and dims_json else [])]
    except Exception as e:
        return {"error": f"Invalid JSON: {e}"}

    ddl_gen = _ensure_ddl_gen(schema_prefix)
    schema = ddl_gen.generate_all_ddl(facts, dims)
    return {
        "status": "success",
        "schema": schema.model_dump(),
        "deploy_script": ddl_gen.generate_deploy_script(schema),
    }


def _emat_deploy(settings, **kwargs) -> Dict[str, Any]:
    ddl_json = kwargs.get("ddl_json", "")
    if not ddl_json:
        return {"error": "ddl_json is required"}
    return {
        "status": "ready",
        "message": "DDL ready for deployment via blce_execute_ddl",
        "ddl": ddl_json,
    }


# ============================================================================
# Dispatch maps
# ============================================================================

_EDW_ACTIONS = {
    "analyze_grain": _edw_analyze_grain,
    "align_facts": _edw_align_facts,
    "generate_union_sql": _edw_generate_union_sql,
    "validate_cross_grain": _edw_validate_cross_grain,
}

_EDIM_ACTIONS = {
    "master_dimension": _edim_master_dimension,
    "generate_key_map": _edim_generate_key_map,
    "detect_overlap": _edim_detect_overlap,
    "generate_dim_ddl": _edim_generate_dim_ddl,
}

_EMAT_ACTIONS = {
    "build_matrix": _emat_build_matrix,
    "conformance_report": _emat_conformance_report,
    "generate_all_ddl": _emat_generate_all_ddl,
    "deploy": _emat_deploy,
}


# ============================================================================
# Unified dispatch functions
# ============================================================================

def dispatch_enterprise_dw(settings, action: str, **kwargs) -> Dict[str, Any]:
    handler = _EDW_ACTIONS.get(action)
    if not handler:
        return {"error": f"Unknown action: '{action}'", "valid_actions": sorted(_EDW_ACTIONS.keys())}
    try:
        return handler(settings, **kwargs)
    except Exception as e:
        logger.error("enterprise_dw(%s) failed: %s", action, e)
        return {"error": f"enterprise_dw({action}) failed: {e}"}


def dispatch_enterprise_dim(settings, action: str, **kwargs) -> Dict[str, Any]:
    handler = _EDIM_ACTIONS.get(action)
    if not handler:
        return {"error": f"Unknown action: '{action}'", "valid_actions": sorted(_EDIM_ACTIONS.keys())}
    try:
        return handler(settings, **kwargs)
    except Exception as e:
        logger.error("enterprise_dim(%s) failed: %s", action, e)
        return {"error": f"enterprise_dim({action}) failed: {e}"}


def dispatch_enterprise_matrix(settings, action: str, **kwargs) -> Dict[str, Any]:
    handler = _EMAT_ACTIONS.get(action)
    if not handler:
        return {"error": f"Unknown action: '{action}'", "valid_actions": sorted(_EMAT_ACTIONS.keys())}
    try:
        return handler(settings, **kwargs)
    except Exception as e:
        logger.error("enterprise_matrix(%s) failed: %s", action, e)
        return {"error": f"enterprise_matrix({action}) failed: {e}"}
