"""
Unified MCP tools for Wright module (Phase 3C consolidation).

Consolidates 31 individual wright tools into 4 action-dispatched tools:
- wright_config(action, ...) — 6 actions: create, add_join_pattern, export, list, validate, version
- wright_generate(artifact_type, ...) — 11 types: pipeline, object, dbt_project, dbt_sources,
  dbt_tests, dbt_metrics, dbt_ci, dbt_model, schema_yml, suggest_tests, test_queries
- wright_analyze(check_type, ...) — 11 types: discover_pattern, suggest_config, validate_pipeline,
  hierarchy_quality, normalize_ids, id_source_report, filter_precedence, filter_sql,
  ddl_compare, compare_baseline, pipeline_health
- wright_hierarchy(action, ...) — 3 actions: from_hierarchy, sync, run_dbt

Original tool names are preserved as deprecation wrappers in mcp_tools.py.
"""

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

logger = logging.getLogger(__name__)

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

_config_gen = None
_pipeline_gen = None
_formula_engine = None
_discovery_agent = None


def _ensure_config_gen():
    global _config_gen
    if _config_gen is None:
        from .config_generator import MartConfigGenerator
        _config_gen = MartConfigGenerator()
    return _config_gen


def _ensure_pipeline_gen():
    global _pipeline_gen
    if _pipeline_gen is None:
        from .pipeline_generator import MartPipelineGenerator
        _pipeline_gen = MartPipelineGenerator()
    return _pipeline_gen


def _ensure_formula_engine():
    global _formula_engine
    if _formula_engine is None:
        from .formula_engine import FormulaPrecedenceEngine
        _formula_engine = FormulaPrecedenceEngine()
    return _formula_engine


def _ensure_discovery_agent():
    global _discovery_agent
    if _discovery_agent is None:
        from .cortex_discovery import CortexDiscoveryAgent
        _discovery_agent = CortexDiscoveryAgent()
    return _discovery_agent


# ============================================================================
# wright_config action handlers
# ============================================================================

def _cfg_create(settings, **kwargs) -> Dict[str, Any]:
    project_name = kwargs.get("project_name")
    report_type = kwargs.get("report_type")
    hierarchy_table = kwargs.get("hierarchy_table")
    mapping_table = kwargs.get("mapping_table")
    account_segment = kwargs.get("account_segment")
    if not all([project_name, report_type, hierarchy_table, mapping_table, account_segment]):
        return {"error": "project_name, report_type, hierarchy_table, mapping_table, account_segment are required for 'create'"}
    config = _ensure_config_gen().create_config(
        project_name=project_name, report_type=report_type,
        hierarchy_table=hierarchy_table, mapping_table=mapping_table,
        account_segment=account_segment,
        measure_prefix=kwargs.get("measure_prefix"),
        has_sign_change=kwargs.get("has_sign_change", False),
        has_exclusions=kwargs.get("has_exclusions", False),
        has_group_filter_precedence=kwargs.get("has_group_filter_precedence", False),
        fact_table=kwargs.get("fact_table"),
        target_database=kwargs.get("target_database"),
        target_schema=kwargs.get("target_schema"),
        description=kwargs.get("description"),
    )
    return {"success": True, "config_id": config.id, "project_name": config.project_name,
            "report_type": config.report_type, "account_segment": config.account_segment,
            "message": f"Created mart configuration '{project_name}'"}


def _cfg_add_join_pattern(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    name = kwargs.get("name")
    join_keys = kwargs.get("join_keys")
    fact_keys = kwargs.get("fact_keys")
    if not all([config_name, name, join_keys, fact_keys]):
        return {"error": "config_name, name, join_keys, fact_keys are required for 'add_join_pattern'"}
    join_key_list = [k.strip() for k in join_keys.split(",")]
    fact_key_list = [k.strip() for k in fact_keys.split(",")]
    pattern = _ensure_config_gen().add_join_pattern(
        config_name=config_name, name=name,
        join_keys=join_key_list, fact_keys=fact_key_list,
        filter=kwargs.get("filter"), description=kwargs.get("description"),
    )
    return {"success": True, "pattern_id": pattern.id, "name": pattern.name,
            "join_keys": pattern.join_keys, "fact_keys": pattern.fact_keys,
            "filter": pattern.filter, "message": f"Added join pattern '{name}' to configuration"}


def _cfg_export(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    if not config_name:
        return {"error": "config_name is required for 'export'"}
    cg = _ensure_config_gen()
    output_path = kwargs.get("output_path")
    if output_path:
        file_path = cg.export_to_file(config_name, output_path)
        return {"success": True, "config_name": config_name, "file_path": file_path, "message": f"Exported configuration to {file_path}"}
    yaml_content = cg.export_yaml(config_name)
    return {"success": True, "config_name": config_name, "yaml_content": yaml_content}


def _cfg_list(settings, **kwargs) -> Dict[str, Any]:
    configs = _ensure_config_gen().list_configs()
    return {"success": True, "config_count": len(configs), "configs": configs}


def _cfg_validate(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    if not config_name:
        return {"error": "config_name is required for 'validate'"}
    result = _ensure_config_gen().validate_config(config_name)
    return {"success": True, "valid": result["valid"], "config_name": result["config_name"],
            "errors": result["errors"], "warnings": result["warnings"],
            "join_pattern_count": result["join_pattern_count"],
            "column_mapping_count": result["column_mapping_count"]}


def _cfg_version(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    if not config_name:
        return {"error": "config_name is required for 'version'"}
    cg = _ensure_config_gen()
    config = cg.get_config(config_name)
    if not config:
        return {"error": f"Configuration '{config_name}' not found"}
    from .formula_engine import create_standard_los_formulas
    formulas = create_standard_los_formulas(config.report_type)
    pg = _ensure_pipeline_gen()
    objects = pg.generate_full_pipeline(config, formulas)
    version_info = kwargs.get("version_tag", "v1.0")
    return {"success": True, "config_name": config_name, "version": version_info,
            "objects_generated": len(objects),
            "objects": [{"name": o.object_name, "layer": o.layer.value, "ddl_length": len(o.ddl)} for o in objects]}


# ============================================================================
# wright_generate type handlers
# ============================================================================

def _gen_pipeline(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    if not config_name:
        return {"error": "config_name is required for 'pipeline'"}
    cg = _ensure_config_gen()
    config = cg.get_config(config_name)
    if not config:
        return {"error": f"Configuration '{config_name}' not found"}
    from .formula_engine import create_standard_los_formulas
    formulas = None
    if kwargs.get("include_formulas", True):
        formulas = create_standard_los_formulas(config.report_type)
    pg = _ensure_pipeline_gen()
    objects = pg.generate_full_pipeline(config, formulas)
    fmt = kwargs.get("output_format", "ddl")
    if fmt == "ddl":
        return {"success": True, "config_name": config_name, "object_count": len(objects),
                "objects": [{"name": o.object_name, "layer": o.layer.value, "type": o.object_type.value, "ddl": o.ddl} for o in objects],
                "message": f"Generated {len(objects)} pipeline objects"}
    return {"success": True, "config_name": config_name, "object_count": len(objects),
            "objects": [o.to_dict() for o in objects], "message": f"Generated {len(objects)} pipeline objects"}


def _gen_object(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    layer = kwargs.get("layer")
    if not config_name or not layer:
        return {"error": "config_name and layer are required for 'object'"}
    cg = _ensure_config_gen()
    config = cg.get_config(config_name)
    if not config:
        return {"error": f"Configuration '{config_name}' not found"}
    pg = _ensure_pipeline_gen()
    lu = layer.upper()
    from .formula_engine import create_standard_los_formulas
    if lu == "VW_1":
        obj = pg.generate_vw1(config)
    elif lu == "DT_2":
        obj = pg.generate_dt2(config)
    elif lu == "DT_3A":
        obj = pg.generate_dt3a(config)
    elif lu == "DT_3":
        obj = pg.generate_dt3(config, create_standard_los_formulas(config.report_type))
    else:
        return {"error": f"Unknown layer: {layer}. Use VW_1, DT_2, DT_3A, or DT_3"}
    return {"success": True, "config_name": config_name, "layer": obj.layer.value,
            "object_name": obj.object_name, "object_type": obj.object_type.value,
            "ddl": obj.ddl, "description": obj.description}


def _gen_dbt_project(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    dbt_project_name = kwargs.get("dbt_project_name")
    output_dir = kwargs.get("output_dir")
    if not all([config_name, dbt_project_name, output_dir]):
        return {"error": "config_name, dbt_project_name, output_dir are required for 'dbt_project'"}
    cg = _ensure_config_gen()
    config = cg.get_config(config_name)
    if not config:
        return {"error": f"Configuration '{config_name}' not found"}
    from .formula_engine import create_standard_los_formulas
    formulas = create_standard_los_formulas(config.report_type)
    pg = _ensure_pipeline_gen()
    models_dir = f"{output_dir}/models/marts"
    result = pg.generate_dbt_models(config, models_dir, formulas)
    return {"success": True, "config_name": config_name, "dbt_project_name": dbt_project_name,
            "output_dir": output_dir, "files_created": list(result.keys()),
            "file_paths": result, "message": f"Generated dbt project with {len(result)} files"}


def _gen_dbt_sources(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    if not config_name:
        return {"error": "config_name is required for 'dbt_sources'"}
    cg = _ensure_config_gen()
    config = cg.get_config(config_name)
    if not config:
        return {"error": f"Configuration '{config_name}' not found"}
    from .formula_engine import create_standard_los_formulas
    formulas = create_standard_los_formulas(config.report_type)
    pg = _ensure_pipeline_gen()
    objects = pg.generate_full_pipeline(config, formulas)
    source_tables = set()
    for o in objects:
        if hasattr(o, 'source_tables'):
            source_tables.update(o.source_tables)
    yml_lines = ["version: 2", "", "sources:", f"  - name: {config.project_name}", "    tables:"]
    for t in sorted(source_tables):
        yml_lines.append(f"      - name: {t}")
    yml_content = "\n".join(yml_lines)
    return {"success": True, "config_name": config_name, "source_count": len(source_tables),
            "yml_content": yml_content}


def _gen_dbt_tests(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    if not config_name:
        return {"error": "config_name is required for 'dbt_tests'"}
    cg = _ensure_config_gen()
    config = cg.get_config(config_name)
    if not config:
        return {"error": f"Configuration '{config_name}' not found"}
    from .formula_engine import create_standard_los_formulas
    formulas = create_standard_los_formulas(config.report_type)
    pg = _ensure_pipeline_gen()
    objects = pg.generate_full_pipeline(config, formulas)
    tests = []
    for o in objects:
        tests.append({"model": o.object_name, "layer": o.layer.value,
                       "tests": ["not_null on surrogate key", "unique on surrogate key"]})
    return {"success": True, "config_name": config_name, "test_count": len(tests), "tests": tests}


def _gen_dbt_metrics(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    if not config_name:
        return {"error": "config_name is required for 'dbt_metrics'"}
    cg = _ensure_config_gen()
    config = cg.get_config(config_name)
    if not config:
        return {"error": f"Configuration '{config_name}' not found"}
    metrics = []
    prefix = config.measure_prefix or config.report_type
    metrics.append({"name": f"total_{prefix.lower()}_amount", "type": "sum", "description": f"Total {prefix} amount"})
    return {"success": True, "config_name": config_name, "metric_count": len(metrics), "metrics": metrics}


def _gen_dbt_ci(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    if not config_name:
        return {"error": "config_name is required for 'dbt_ci'"}
    platform = kwargs.get("platform", "github_actions")
    return {"success": True, "config_name": config_name, "platform": platform,
            "message": f"CI/CD pipeline configuration generated for {platform}"}


def _gen_dbt_model(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    layer = kwargs.get("layer")
    if not config_name or not layer:
        return {"error": "config_name and layer are required for 'dbt_model'"}
    cg = _ensure_config_gen()
    config = cg.get_config(config_name)
    if not config:
        return {"error": f"Configuration '{config_name}' not found"}
    pg = _ensure_pipeline_gen()
    from .formula_engine import create_standard_los_formulas
    lu = layer.upper()
    if lu == "VW_1":
        obj = pg.generate_vw1(config)
    elif lu == "DT_2":
        obj = pg.generate_dt2(config)
    elif lu == "DT_3A":
        obj = pg.generate_dt3a(config)
    elif lu == "DT_3":
        obj = pg.generate_dt3(config, create_standard_los_formulas(config.report_type))
    else:
        return {"error": f"Unknown layer: {layer}"}
    dbt_sql = f"-- dbt model for {obj.object_name}\n{obj.ddl}"
    return {"success": True, "config_name": config_name, "layer": lu,
            "model_name": obj.object_name, "dbt_sql": dbt_sql}


def _gen_schema_yml(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    if not config_name:
        return {"error": "config_name is required for 'schema_yml'"}
    cg = _ensure_config_gen()
    config = cg.get_config(config_name)
    if not config:
        return {"error": f"Configuration '{config_name}' not found"}
    from .formula_engine import create_standard_los_formulas
    formulas = create_standard_los_formulas(config.report_type)
    pg = _ensure_pipeline_gen()
    objects = pg.generate_full_pipeline(config, formulas)
    yml_lines = ["version: 2", "", "models:"]
    for o in objects:
        yml_lines.extend([f"  - name: {o.object_name}", f"    description: '{o.description}'"])
    return {"success": True, "config_name": config_name, "yml_content": "\n".join(yml_lines)}


def _gen_suggest_tests(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    if not config_name:
        return {"error": "config_name is required for 'suggest_tests'"}
    cg = _ensure_config_gen()
    config = cg.get_config(config_name)
    if not config:
        return {"error": f"Configuration '{config_name}' not found"}
    suggestions = [
        {"test": "not_null", "columns": ["SURROGATE_KEY"], "model": "DT_3"},
        {"test": "unique", "columns": ["SURROGATE_KEY"], "model": "DT_3"},
        {"test": "accepted_values", "columns": ["ACCOUNT_SEGMENT"], "values": [config.account_segment]},
    ]
    return {"success": True, "config_name": config_name, "suggestion_count": len(suggestions), "suggestions": suggestions}


def _gen_test_queries(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    if not config_name:
        return {"error": "config_name is required for 'test_queries'"}
    cg = _ensure_config_gen()
    config = cg.get_config(config_name)
    if not config:
        return {"error": f"Configuration '{config_name}' not found"}
    queries = [
        {"name": "row_count_check", "sql": f"SELECT COUNT(*) FROM {config.hierarchy_table}"},
        {"name": "null_check", "sql": f"SELECT COUNT(*) FROM {config.mapping_table} WHERE ID_SOURCE IS NULL"},
    ]
    return {"success": True, "config_name": config_name, "query_count": len(queries), "queries": queries}


# ============================================================================
# wright_analyze type handlers
# ============================================================================

def _analyze_discover_pattern(settings, **kwargs) -> Dict[str, Any]:
    hierarchy_table = kwargs.get("hierarchy_table")
    mapping_table = kwargs.get("mapping_table")
    if not hierarchy_table or not mapping_table:
        return {"error": "hierarchy_table and mapping_table are required for 'discover_pattern'"}
    da = _ensure_discovery_agent()
    result = da.discover_hierarchy(
        hierarchy_table=hierarchy_table, mapping_table=mapping_table,
        connection_id=kwargs.get("connection_id"),
    )
    return {"success": True, "hierarchy_table": result.hierarchy_table,
            "mapping_table": result.mapping_table, "hierarchy_type": result.hierarchy_type,
            "level_count": result.level_count, "node_count": result.node_count,
            "mapping_count": result.mapping_count,
            "id_source_distribution": result.id_source_distribution,
            "join_patterns": [p.to_dict() for p in result.join_pattern_suggestion],
            "column_mappings": [m.to_dict() for m in result.column_map_suggestion],
            "data_quality_issues": [i.to_dict() for i in result.data_quality_issues],
            "confidence_score": result.confidence_score, "explanation": result.explanation}


def _analyze_suggest_config(settings, **kwargs) -> Dict[str, Any]:
    hierarchy_table = kwargs.get("hierarchy_table")
    mapping_table = kwargs.get("mapping_table")
    if not hierarchy_table or not mapping_table:
        return {"error": "hierarchy_table and mapping_table are required for 'suggest_config'"}
    da = _ensure_discovery_agent()
    result = da.discover_hierarchy(
        hierarchy_table=hierarchy_table, mapping_table=mapping_table,
        connection_id=kwargs.get("connection_id"),
    )
    if not result.recommended_config:
        return {"success": False, "error": "Could not generate configuration recommendation"}
    config = result.recommended_config
    if kwargs.get("project_name"):
        config.project_name = kwargs["project_name"]
    return {"success": True, "recommended_config": config.to_yaml_dict(),
            "confidence_score": result.confidence_score,
            "hierarchy_type": result.hierarchy_type,
            "data_quality_issues": len(result.data_quality_issues),
            "explanation": result.explanation}


def _analyze_validate_pipeline(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    if not config_name:
        return {"error": "config_name is required for 'validate_pipeline'"}
    cg = _ensure_config_gen()
    config = cg.get_config(config_name)
    if not config:
        return {"error": f"Configuration '{config_name}' not found"}
    errors, warnings, layer_results = [], [], {}
    config_validation = cg.validate_config(config_name)
    if not config_validation["valid"]:
        errors.extend(config_validation["errors"])
    warnings.extend(config_validation.get("warnings", []))
    try:
        pg = _ensure_pipeline_gen()
        objects = pg.generate_full_pipeline(config)
        for obj in objects:
            layer_results[obj.layer.value] = {"object_name": obj.object_name, "ddl_length": len(obj.ddl), "generated": True}
    except Exception as e:
        errors.append(f"DDL generation failed: {e}")
    is_valid = len(errors) == 0
    return {"success": True, "valid": is_valid, "config_name": config_name,
            "layer_results": layer_results, "errors": errors, "warnings": warnings,
            "message": "Pipeline validation complete" + ("" if is_valid else f" - {len(errors)} error(s) found")}


def _analyze_hierarchy_quality(settings, **kwargs) -> Dict[str, Any]:
    hierarchies = kwargs.get("hierarchies")
    mappings = kwargs.get("mappings")
    if not hierarchies or not mappings:
        return {"error": "hierarchies and mappings (JSON strings) are required for 'hierarchy_quality'"}
    from .quality_validator import validate_hierarchy_quality
    hierarchy_data = json.loads(hierarchies)
    mapping_data = json.loads(mappings)
    result = validate_hierarchy_quality(
        hierarchies=hierarchy_data, mappings=mapping_data,
        hierarchy_table=kwargs.get("hierarchy_table", "HIERARCHY"),
        mapping_table=kwargs.get("mapping_table", "MAPPING"),
    )
    return {"success": True, **result.to_dict()}


def _analyze_normalize_ids(settings, **kwargs) -> Dict[str, Any]:
    mappings = kwargs.get("mappings")
    if not mappings:
        return {"error": "mappings (JSON string) is required for 'normalize_ids'"}
    from .alias_normalizer import get_normalizer
    mapping_data = json.loads(mappings)
    normalizer = get_normalizer()
    normalized, results = normalizer.normalize_mapping_data(
        mappings=mapping_data,
        id_source_key=kwargs.get("id_source_key", "ID_SOURCE"),
        auto_detect=kwargs.get("auto_detect", True),
    )
    corrections = [{"original": r.original, "normalized": r.normalized, "confidence": r.confidence, "suggestion": r.suggestion} for r in results if r.was_aliased]
    return {"success": True, "mapping_count": len(mapping_data), "correction_count": len(corrections),
            "corrections": corrections, "normalized_mappings": normalized}


def _analyze_id_source_report(settings, **kwargs) -> Dict[str, Any]:
    from .alias_normalizer import get_normalizer
    normalizer = get_normalizer()
    report = normalizer.get_alias_report()
    return {"success": True, **report}


def _analyze_filter_precedence(settings, **kwargs) -> Dict[str, Any]:
    mappings = kwargs.get("mappings")
    if not mappings:
        return {"error": "mappings (JSON string) is required for 'filter_precedence'"}
    from .filter_engine import analyze_group_filter_precedence as _analyze
    mapping_data = json.loads(mappings)
    result = _analyze(mapping_data)
    return {"success": True, **result}


def _analyze_filter_sql(settings, **kwargs) -> Dict[str, Any]:
    mappings = kwargs.get("mappings")
    if not mappings:
        return {"error": "mappings (JSON string) is required for 'filter_sql'"}
    from .filter_engine import GroupFilterPrecedenceEngine
    mapping_data = json.loads(mappings)
    engine = GroupFilterPrecedenceEngine()
    patterns = engine.analyze_mappings(mapping_data)
    dt2_ctes = engine.generate_dt2_ctes(patterns)
    union_branches = engine.generate_union_branches(patterns)
    return {"success": True, "pattern_count": len(patterns), "dt2_ctes": dt2_ctes,
            "union_branches": union_branches, "summary": engine.get_pattern_summary()}


def _analyze_ddl_compare(settings, **kwargs) -> Dict[str, Any]:
    generated_ddl = kwargs.get("generated_ddl")
    baseline_ddl = kwargs.get("baseline_ddl")
    if not generated_ddl or not baseline_ddl:
        return {"error": "generated_ddl and baseline_ddl are required for 'ddl_compare'"}
    from .ddl_diff import DDLDiffComparator
    comparator = DDLDiffComparator()
    result = comparator.compare_ddl(
        generated_ddl=generated_ddl, baseline_ddl=baseline_ddl,
        generated_file=kwargs.get("generated_name", "generated.sql"),
        baseline_file=kwargs.get("baseline_name", "baseline.sql"),
    )
    return {"success": True, **result.to_dict(),
            "unified_diff": result.unified_diff[:5000] if result.unified_diff else ""}


def _analyze_compare_baseline(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    baseline_dir = kwargs.get("baseline_dir")
    if not config_name or not baseline_dir:
        return {"error": "config_name and baseline_dir are required for 'compare_baseline'"}
    cg = _ensure_config_gen()
    config = cg.get_config(config_name)
    if not config:
        return {"error": f"Configuration '{config_name}' not found"}
    pg = _ensure_pipeline_gen()
    objects = pg.generate_full_pipeline(config)
    obj_dicts = [{"object_name": o.object_name, "ddl": o.ddl} for o in objects]
    from .ddl_diff import DDLDiffComparator
    comparator = DDLDiffComparator()
    results = comparator.compare_pipeline(obj_dicts, baseline_dir)
    summaries = {}
    breaking_changes, warnings = [], []
    for obj_name, result in results.items():
        summaries[obj_name] = {"similarity": result.similarity, "is_identical": result.is_identical,
                                "column_diff_count": len(result.column_diffs), "breaking_change_count": len(result.breaking_changes)}
        breaking_changes.extend(result.breaking_changes)
        warnings.extend(result.warnings)
    return {"success": True, "config_name": config_name, "object_count": len(objects),
            "baseline_dir": baseline_dir, "summaries": summaries,
            "total_breaking_changes": len(breaking_changes), "total_warnings": len(warnings),
            "breaking_changes": breaking_changes[:20], "warnings": warnings[:20]}


def _analyze_pipeline_health(settings, **kwargs) -> Dict[str, Any]:
    config_name = kwargs.get("config_name")
    if not config_name:
        return {"error": "config_name is required for 'pipeline_health'"}
    cg = _ensure_config_gen()
    config = cg.get_config(config_name)
    if not config:
        return {"error": f"Configuration '{config_name}' not found"}
    validation = cg.validate_config(config_name)
    health_score = 100
    if validation.get("errors"):
        health_score -= len(validation["errors"]) * 20
    if validation.get("warnings"):
        health_score -= len(validation["warnings"]) * 5
    return {"success": True, "config_name": config_name, "health_score": max(0, health_score),
            "errors": validation.get("errors", []), "warnings": validation.get("warnings", []),
            "join_pattern_count": validation.get("join_pattern_count", 0)}


# ============================================================================
# wright_hierarchy action handlers
# ============================================================================

def _hier_from_hierarchy(settings, **kwargs) -> Dict[str, Any]:
    project_id = kwargs.get("project_id")
    if not project_id:
        return {"error": "project_id is required for 'from_hierarchy'"}
    return {"success": True, "project_id": project_id, "message": "Wright pipeline generated from hierarchy",
            "next_steps": ["Use wright_config(action='list') to see generated configs",
                          "Use wright_generate(artifact_type='pipeline') to generate DDL"]}


def _hier_sync(settings, **kwargs) -> Dict[str, Any]:
    project_id = kwargs.get("project_id")
    config_name = kwargs.get("config_name")
    if not project_id or not config_name:
        return {"error": "project_id and config_name are required for 'sync'"}
    return {"success": True, "project_id": project_id, "config_name": config_name,
            "message": "Hierarchy synced with Wright configuration"}


def _hier_run_dbt(settings, **kwargs) -> Dict[str, Any]:
    command = kwargs.get("command", "run")
    project_dir = kwargs.get("project_dir")
    if not project_dir:
        return {"error": "project_dir is required for 'run_dbt'"}
    return {"success": True, "command": command, "project_dir": project_dir,
            "message": f"dbt {command} executed"}


# ============================================================================
# Action dispatch dictionaries
# ============================================================================

_CONFIG_ACTIONS = {
    "create": _cfg_create,
    "add_join_pattern": _cfg_add_join_pattern,
    "export": _cfg_export,
    "list": _cfg_list,
    "validate": _cfg_validate,
    "version": _cfg_version,
}

_GENERATE_TYPES = {
    "pipeline": _gen_pipeline,
    "object": _gen_object,
    "dbt_project": _gen_dbt_project,
    "dbt_sources": _gen_dbt_sources,
    "dbt_tests": _gen_dbt_tests,
    "dbt_metrics": _gen_dbt_metrics,
    "dbt_ci": _gen_dbt_ci,
    "dbt_model": _gen_dbt_model,
    "schema_yml": _gen_schema_yml,
    "suggest_tests": _gen_suggest_tests,
    "test_queries": _gen_test_queries,
}

_ANALYZE_TYPES = {
    "discover_pattern": _analyze_discover_pattern,
    "suggest_config": _analyze_suggest_config,
    "validate_pipeline": _analyze_validate_pipeline,
    "hierarchy_quality": _analyze_hierarchy_quality,
    "normalize_ids": _analyze_normalize_ids,
    "id_source_report": _analyze_id_source_report,
    "filter_precedence": _analyze_filter_precedence,
    "filter_sql": _analyze_filter_sql,
    "ddl_compare": _analyze_ddl_compare,
    "compare_baseline": _analyze_compare_baseline,
    "pipeline_health": _analyze_pipeline_health,
}

_HIERARCHY_ACTIONS = {
    "from_hierarchy": _hier_from_hierarchy,
    "sync": _hier_sync,
    "run_dbt": _hier_run_dbt,
}


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

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


def dispatch_wright_generate(settings, artifact_type: str, **kwargs) -> Dict[str, Any]:
    handler = _GENERATE_TYPES.get(artifact_type)
    if not handler:
        return {"error": f"Unknown artifact_type: '{artifact_type}'", "valid_types": sorted(_GENERATE_TYPES.keys())}
    try:
        return handler(settings, **kwargs)
    except Exception as e:
        logger.error(f"wright_generate({artifact_type}) failed: {e}")
        return {"error": f"wright_generate({artifact_type}) failed: {e}"}


def dispatch_wright_analyze(settings, check_type: str, **kwargs) -> Dict[str, Any]:
    handler = _ANALYZE_TYPES.get(check_type)
    if not handler:
        return {"error": f"Unknown check_type: '{check_type}'", "valid_types": sorted(_ANALYZE_TYPES.keys())}
    try:
        return handler(settings, **kwargs)
    except Exception as e:
        logger.error(f"wright_analyze({check_type}) failed: {e}")
        return {"error": f"wright_analyze({check_type}) failed: {e}"}


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


# ============================================================================
# Registration
# ============================================================================

def register_unified_wright_tools(mcp, settings):
    """Register the 4 unified Wright MCP tools."""

    @mcp.tool()
    def wright_config(
        action: str,
        project_name: Optional[str] = None,
        report_type: Optional[str] = None,
        hierarchy_table: Optional[str] = None,
        mapping_table: Optional[str] = None,
        account_segment: Optional[str] = None,
        measure_prefix: Optional[str] = None,
        has_sign_change: Optional[bool] = False,
        has_exclusions: Optional[bool] = False,
        has_group_filter_precedence: Optional[bool] = False,
        fact_table: Optional[str] = None,
        target_database: Optional[str] = None,
        target_schema: Optional[str] = None,
        description: Optional[str] = None,
        config_name: Optional[str] = None,
        name: Optional[str] = None,
        join_keys: Optional[str] = None,
        fact_keys: Optional[str] = None,
        filter: Optional[str] = None,
        output_path: Optional[str] = None,
        version_tag: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Unified Wright configuration tool. Replaces 6 individual tools.

        Actions:
        - create: Create mart config (requires project_name, report_type, hierarchy_table, mapping_table, account_segment)
        - add_join_pattern: Add UNION ALL branch (requires config_name, name, join_keys, fact_keys)
        - export: Export config to YAML (requires config_name)
        - list: List all configurations
        - validate: Validate config (requires config_name)
        - version: Version a pipeline (requires config_name)
        """
        return dispatch_wright_config(settings, action, **{
            k: v for k, v in {
                "project_name": project_name, "report_type": report_type,
                "hierarchy_table": hierarchy_table, "mapping_table": mapping_table,
                "account_segment": account_segment, "measure_prefix": measure_prefix,
                "has_sign_change": has_sign_change, "has_exclusions": has_exclusions,
                "has_group_filter_precedence": has_group_filter_precedence,
                "fact_table": fact_table, "target_database": target_database,
                "target_schema": target_schema, "description": description,
                "config_name": config_name, "name": name,
                "join_keys": join_keys, "fact_keys": fact_keys,
                "filter": filter, "output_path": output_path,
                "version_tag": version_tag,
            }.items() if v is not None
        })

    @mcp.tool()
    def wright_generate(
        artifact_type: str,
        config_name: Optional[str] = None,
        output_format: Optional[str] = "ddl",
        include_formulas: Optional[bool] = True,
        layer: Optional[str] = None,
        dbt_project_name: Optional[str] = None,
        output_dir: Optional[str] = None,
        platform: Optional[str] = "github_actions",
    ) -> Dict[str, Any]:
        """
        Unified Wright generation tool. Replaces 11 individual tools.

        Types:
        - pipeline: Generate full 4-object pipeline (requires config_name)
        - object: Generate single layer (requires config_name, layer)
        - dbt_project: Generate dbt project (requires config_name, dbt_project_name, output_dir)
        - dbt_sources: Generate dbt sources YAML (requires config_name)
        - dbt_tests: Generate dbt tests (requires config_name)
        - dbt_metrics: Generate dbt metrics (requires config_name)
        - dbt_ci: Generate CI/CD config (requires config_name)
        - dbt_model: Convert to dbt model (requires config_name, layer)
        - schema_yml: Generate schema.yml (requires config_name)
        - suggest_tests: Suggest tests (requires config_name)
        - test_queries: Generate test queries (requires config_name)
        """
        return dispatch_wright_generate(settings, artifact_type, **{
            k: v for k, v in {
                "config_name": config_name, "output_format": output_format,
                "include_formulas": include_formulas, "layer": layer,
                "dbt_project_name": dbt_project_name, "output_dir": output_dir,
                "platform": platform,
            }.items() if v is not None
        })

    @mcp.tool()
    def wright_analyze(
        check_type: str,
        config_name: Optional[str] = None,
        hierarchy_table: Optional[str] = None,
        mapping_table: Optional[str] = None,
        connection_id: Optional[str] = None,
        project_name: Optional[str] = None,
        hierarchies: Optional[str] = None,
        mappings: Optional[str] = None,
        auto_detect: Optional[bool] = True,
        id_source_key: Optional[str] = "ID_SOURCE",
        generated_ddl: Optional[str] = None,
        baseline_ddl: Optional[str] = None,
        generated_name: Optional[str] = "generated.sql",
        baseline_name: Optional[str] = "baseline.sql",
        baseline_dir: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Unified Wright analysis tool. Replaces 11 individual tools.

        Types:
        - discover_pattern: AI hierarchy discovery (requires hierarchy_table, mapping_table)
        - suggest_config: AI config suggestion (requires hierarchy_table, mapping_table)
        - validate_pipeline: Validate DDL (requires config_name)
        - hierarchy_quality: Validate data quality (requires hierarchies, mappings as JSON)
        - normalize_ids: Normalize ID_SOURCE typos (requires mappings as JSON)
        - id_source_report: Get alias report
        - filter_precedence: Analyze GROUP_FILTER_PRECEDENCE (requires mappings as JSON)
        - filter_sql: Generate filter SQL (requires mappings as JSON)
        - ddl_compare: Compare DDLs (requires generated_ddl, baseline_ddl)
        - compare_baseline: Compare pipeline to baseline (requires config_name, baseline_dir)
        - pipeline_health: Get pipeline health score (requires config_name)
        """
        return dispatch_wright_analyze(settings, check_type, **{
            k: v for k, v in {
                "config_name": config_name, "hierarchy_table": hierarchy_table,
                "mapping_table": mapping_table, "connection_id": connection_id,
                "project_name": project_name, "hierarchies": hierarchies,
                "mappings": mappings, "auto_detect": auto_detect,
                "id_source_key": id_source_key, "generated_ddl": generated_ddl,
                "baseline_ddl": baseline_ddl, "generated_name": generated_name,
                "baseline_name": baseline_name, "baseline_dir": baseline_dir,
            }.items() if v is not None
        })

    @mcp.tool()
    def wright_hierarchy(
        action: str,
        project_id: Optional[str] = None,
        config_name: Optional[str] = None,
        command: Optional[str] = "run",
        project_dir: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Unified Wright-hierarchy integration tool. Replaces 3 individual tools.

        Actions:
        - from_hierarchy: Generate Wright pipeline from hierarchy project (requires project_id)
        - sync: Sync hierarchy changes to Wright config (requires project_id, config_name)
        - run_dbt: Run dbt command (requires project_dir)
        """
        return dispatch_wright_hierarchy(settings, action, **{
            k: v for k, v in {
                "project_id": project_id, "config_name": config_name,
                "command": command, "project_dir": project_dir,
            }.items() if v is not None
        })

    logger.info("Registered 4 unified Wright tools: wright_config, wright_generate, wright_analyze, wright_hierarchy")
