"""
Unified MCP tools for Faux Objects (Phase 3B consolidation).

Consolidates 18 individual faux tools into 2 action-dispatched tools:
- faux_project(action, ...) — 10 actions for project/object management
- semantic_view(action, ...) — 8 actions for semantic view definition & SQL translation

Original tool names are preserved as deprecation wrappers in mcp_tools.py.
Note: Legacy wrappers return json.dumps() strings; unified tools return dicts.
"""

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

logger = logging.getLogger(__name__)

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

_service = None
_sql_translator = None


def _ensure_service(data_dir: str):
    global _service
    if _service is None:
        from .service import FauxObjectsService
        _service = FauxObjectsService(data_dir)
    return _service


def _ensure_translator():
    global _sql_translator
    if _sql_translator is None:
        from .sql_translator import SQLTranslator
        _sql_translator = SQLTranslator()
    return _sql_translator


def _find_faux_project_by_hierarchy_id(service, hierarchy_project_id: str):
    """Find an existing faux project linked to a hierarchy project."""
    for summary in service.list_projects():
        faux_id = summary.get("id")
        if not faux_id:
            continue
        project = service.get_project(faux_id)
        if project and getattr(project, "hierarchy_project_id", None) == hierarchy_project_id:
            return project
    return None


def _resolve_semantic_project_id(data_dir: str, project_id: str):
    """Resolve a semantic_view project_id across faux + hierarchy systems."""
    service = _ensure_service(data_dir)
    faux_project = service.get_project(project_id)
    if faux_project:
        return faux_project.id, []

    try:
        from src.hierarchy.service import HierarchyService

        hierarchy_service = HierarchyService(data_dir)
        hierarchy_project = hierarchy_service.get_project(project_id)
    except Exception:
        hierarchy_project = None

    if not hierarchy_project:
        raise ValueError(
            f"Project '{project_id}' not found in faux_project or hierarchy_manage. "
            "Use faux_project(action='create', ...) first, or pass a valid hierarchy project_id."
        )

    linked = _find_faux_project_by_hierarchy_id(service, project_id)
    if linked:
        return linked.id, [
            f"Mapped hierarchy project '{project_id}' to existing faux project '{linked.id}'."
        ]

    linked_project = service.create_project(
        hierarchy_project.get("name", f"hier_{project_id}"),
        description=f"Auto-linked from hierarchy project {project_id}",
    )
    linked_project.hierarchy_project_id = project_id
    linked_project.hierarchy_project_name = hierarchy_project.get("name")
    service._save_project(linked_project)
    return linked_project.id, [
        f"Auto-created faux project '{linked_project.id}' from hierarchy project '{project_id}'."
    ]


# ============================================================================
# faux_project action handlers
# ============================================================================

def _fp_create(data_dir, **kwargs) -> Dict[str, Any]:
    name = kwargs.get("name")
    if not name:
        return {"error": "name is required for 'create' action"}
    service = _ensure_service(data_dir)
    project = service.create_project(name, kwargs.get("description", ""))
    return {
        "status": "success",
        "project": {"id": project.id, "name": project.name, "description": project.description},
        "next_step": "Use semantic_view(action='define', ...) to define the semantic view this project wraps",
    }


def _fp_list(data_dir, **kwargs) -> Dict[str, Any]:
    service = _ensure_service(data_dir)
    projects = service.list_projects()
    return {"status": "success", "count": len(projects), "projects": projects}


def _fp_get(data_dir, **kwargs) -> Dict[str, Any]:
    project_id = kwargs.get("project_id")
    if not project_id:
        return {"error": "project_id is required for 'get' action"}
    service = _ensure_service(data_dir)
    project = service.get_project(project_id)
    if not project:
        return {"error": f"Project {project_id} not found"}

    result = {
        "status": "success",
        "project": {
            "id": project.id, "name": project.name,
            "description": project.description, "created_at": str(project.created_at),
        },
    }
    if project.semantic_view:
        sv = project.semantic_view
        result["semantic_view"] = {
            "name": sv.fully_qualified_name,
            "tables": [{"alias": t.alias, "table": t.fully_qualified_name} for t in sv.tables],
            "dimensions": [{"name": c.name, "type": c.data_type, "alias": c.table_alias} for c in sv.dimensions],
            "metrics": [{"name": c.name, "type": c.data_type, "expression": c.expression} for c in sv.metrics],
            "facts": [{"name": c.name, "type": c.data_type} for c in sv.facts],
            "relationships": [{"from": f"{r.from_table}.{r.from_column}", "to": r.to_table} for r in sv.relationships],
        }
    result["faux_objects"] = [
        {
            "name": o.fully_qualified_name, "type": o.faux_type.value,
            "dimensions": o.selected_dimensions, "metrics": o.selected_metrics,
        }
        for o in project.faux_objects
    ]
    return result


def _fp_delete(data_dir, **kwargs) -> Dict[str, Any]:
    project_id = kwargs.get("project_id")
    if not project_id:
        return {"error": "project_id is required for 'delete' action"}
    service = _ensure_service(data_dir)
    success = service.delete_project(project_id)
    if success:
        return {"status": "success", "message": f"Project {project_id} deleted"}
    return {"error": f"Project {project_id} not found"}


def _fp_add_object(data_dir, **kwargs) -> Dict[str, Any]:
    project_id = kwargs.get("project_id")
    name = kwargs.get("name")
    faux_type = kwargs.get("faux_type")
    target_database = kwargs.get("target_database")
    target_schema = kwargs.get("target_schema")
    if not all([project_id, name, faux_type, target_database, target_schema]):
        return {"error": "project_id, name, faux_type, target_database, target_schema are required for 'add_object'"}

    service = _ensure_service(data_dir)

    selected_dimensions = kwargs.get("selected_dimensions")
    selected_metrics = kwargs.get("selected_metrics")
    selected_facts = kwargs.get("selected_facts")
    dims = [s.strip() for s in selected_dimensions.split(",") if s.strip()] if selected_dimensions else None
    mets = [s.strip() for s in selected_metrics.split(",") if s.strip()] if selected_metrics else None
    facts = [s.strip() for s in selected_facts.split(",") if s.strip()] if selected_facts else None

    parameters = kwargs.get("parameters")
    params = json.loads(parameters) if parameters else None

    project = service.add_faux_object(
        project_id, name, faux_type, target_database, target_schema,
        selected_dimensions=dims, selected_metrics=mets, selected_facts=facts,
        parameters=params,
        warehouse=kwargs.get("warehouse") or None,
        target_lag=kwargs.get("target_lag") or None,
        schedule=kwargs.get("schedule") or None,
        materialized_table=kwargs.get("materialized_table") or None,
        where_clause=kwargs.get("where_clause") or None,
        comment=kwargs.get("comment") or None,
    )
    return {
        "status": "success",
        "faux_object": {"name": f"{target_database}.{target_schema}.{name}", "type": faux_type},
        "total_faux_objects": len(project.faux_objects),
        "next_step": "Use faux_project(action='generate_scripts', ...) to generate SQL for all objects",
    }


def _fp_remove_object(data_dir, **kwargs) -> Dict[str, Any]:
    project_id = kwargs.get("project_id")
    object_name = kwargs.get("object_name")
    if not project_id or not object_name:
        return {"error": "project_id and object_name are required for 'remove_object'"}
    service = _ensure_service(data_dir)
    project = service.remove_faux_object(project_id, object_name)
    return {"status": "success", "removed": object_name, "remaining": len(project.faux_objects)}


def _fp_generate_scripts(data_dir, **kwargs) -> Dict[str, Any]:
    project_id = kwargs.get("project_id")
    if not project_id:
        return {"error": "project_id is required for 'generate_scripts'"}
    service = _ensure_service(data_dir)
    scripts = service.generate_all_scripts(project_id)
    return {
        "status": "success",
        "count": len(scripts),
        "scripts": [
            {
                "object_name": s.object_name, "object_type": s.object_type.value,
                "sql_preview": s.sql[:500] + ("..." if len(s.sql) > 500 else ""),
                "sql_length": len(s.sql), "dependencies": s.dependencies,
            }
            for s in scripts
        ],
    }


def _fp_generate_bundle(data_dir, **kwargs) -> Dict[str, Any]:
    project_id = kwargs.get("project_id")
    if not project_id:
        return {"error": "project_id is required for 'generate_bundle'"}
    service = _ensure_service(data_dir)
    bundle = service.generate_deployment_bundle(project_id)
    return {"status": "success", "bundle_length": len(bundle), "bundle_sql": bundle}


def _fp_generate_ddl(data_dir, **kwargs) -> Dict[str, Any]:
    project_id = kwargs.get("project_id")
    if not project_id:
        return {"error": "project_id is required for 'generate_ddl'"}
    service = _ensure_service(data_dir)
    project = service.get_project(project_id)
    if not project or not project.semantic_view:
        return {"error": f"Project {project_id} not found or no semantic view defined"}
    ddl = service.generate_semantic_view_ddl(project.semantic_view)
    return {"status": "success", "semantic_view": project.semantic_view.fully_qualified_name, "ddl": ddl}


def _fp_export(data_dir, **kwargs) -> Dict[str, Any]:
    project_id = kwargs.get("project_id")
    if not project_id:
        return {"error": "project_id is required for 'export'"}
    service = _ensure_service(data_dir)
    output_dir = kwargs.get("output_dir")
    if not output_dir:
        output_dir = os.path.join(service.data_dir, "faux_objects", "exports", project_id)
    exported = service.export_scripts(project_id, output_dir)
    return {"status": "success", "files_exported": len(exported), "output_dir": output_dir, "files": exported}


# ============================================================================
# semantic_view action handlers
# ============================================================================

def _sv_define(data_dir, **kwargs) -> Dict[str, Any]:
    project_id = kwargs.get("project_id")
    name = kwargs.get("name")
    database = kwargs.get("database")
    schema_name = kwargs.get("schema_name")
    if not all([project_id, name, database, schema_name]):
        return {"error": "project_id, name, database, schema_name are required for 'define'"}
    resolved_project_id, notes = _resolve_semantic_project_id(data_dir, project_id)
    service = _ensure_service(data_dir)
    service.define_semantic_view(
        resolved_project_id, name, database, schema_name,
        comment=kwargs.get("comment") or None,
        ai_sql_generation=kwargs.get("ai_sql_generation") or None,
    )
    result = {
        "status": "success",
        "project_id": resolved_project_id,
        "semantic_view": f"{database}.{schema_name}.{name}",
        "next_steps": [
            "Add tables: semantic_view(action='add_table', ...)",
            "Add columns: semantic_view(action='add_column', ...) with column_type='dimension'|'metric'|'fact'",
            "Add relationships: semantic_view(action='add_relationship', ...)",
        ],
    }
    if notes:
        result["notes"] = notes
    return result


def _sv_add_table(data_dir, **kwargs) -> Dict[str, Any]:
    project_id = kwargs.get("project_id")
    alias = kwargs.get("alias")
    fully_qualified_name = kwargs.get("fully_qualified_name")
    if not all([project_id, alias, fully_qualified_name]):
        return {"error": "project_id, alias, fully_qualified_name are required for 'add_table'"}
    resolved_project_id, notes = _resolve_semantic_project_id(data_dir, project_id)
    service = _ensure_service(data_dir)
    project = service.add_semantic_table(
        resolved_project_id, alias, fully_qualified_name,
        primary_key=kwargs.get("primary_key") or None,
    )
    result = {"status": "success", "project_id": resolved_project_id, "table_added": alias, "total_tables": len(project.semantic_view.tables)}
    if notes:
        result["notes"] = notes
    return result


def _sv_add_column(data_dir, **kwargs) -> Dict[str, Any]:
    project_id = kwargs.get("project_id")
    name = kwargs.get("name")
    column_type = kwargs.get("column_type")
    if not all([project_id, name, column_type]):
        return {"error": "project_id, name, column_type are required for 'add_column'"}
    resolved_project_id, notes = _resolve_semantic_project_id(data_dir, project_id)
    service = _ensure_service(data_dir)

    synonyms_str = kwargs.get("synonyms", "")
    syn_list = [s.strip() for s in synonyms_str.split(",") if s.strip()] if synonyms_str else []

    project = service.add_semantic_column(
        resolved_project_id, name, column_type,
        data_type=kwargs.get("data_type", "VARCHAR"),
        table_alias=kwargs.get("table_alias") or None,
        expression=kwargs.get("expression") or None,
        synonyms=syn_list,
        comment=kwargs.get("comment") or None,
    )
    sv = project.semantic_view
    result = {
        "status": "success",
        "project_id": resolved_project_id,
        "column_added": name, "column_type": column_type,
        "counts": {"dimensions": len(sv.dimensions), "metrics": len(sv.metrics), "facts": len(sv.facts)},
    }
    if notes:
        result["notes"] = notes
    return result


def _sv_add_relationship(data_dir, **kwargs) -> Dict[str, Any]:
    project_id = kwargs.get("project_id")
    from_table = kwargs.get("from_table")
    from_column = kwargs.get("from_column")
    to_table = kwargs.get("to_table")
    if not all([project_id, from_table, from_column, to_table]):
        return {"error": "project_id, from_table, from_column, to_table are required for 'add_relationship'"}
    resolved_project_id, notes = _resolve_semantic_project_id(data_dir, project_id)
    service = _ensure_service(data_dir)
    project = service.add_semantic_relationship(
        resolved_project_id, from_table, from_column, to_table,
        to_column=kwargs.get("to_column") or None,
    )
    result = {
        "status": "success",
        "project_id": resolved_project_id,
        "relationship_added": f"{from_table}.{from_column} -> {to_table}",
        "total_relationships": len(project.semantic_view.relationships),
    }
    if notes:
        result["notes"] = notes
    return result


def _sv_detect_sql(data_dir, **kwargs) -> Dict[str, Any]:
    sql = kwargs.get("sql")
    if not sql:
        return {"error": "sql is required for 'detect_sql'"}
    from .sql_translator import SQLInputFormat
    translator = _ensure_translator()
    format_type = translator.detect_format(sql)
    descriptions = {
        SQLInputFormat.CREATE_VIEW: "CREATE VIEW statement wrapping a query",
        SQLInputFormat.SELECT_QUERY: "SELECT query (may contain aggregations)",
        SQLInputFormat.CREATE_SEMANTIC_VIEW: "CREATE SEMANTIC VIEW DDL statement",
        SQLInputFormat.UNKNOWN: "Unknown SQL format",
    }
    return {"status": "success", "format": format_type.value, "description": descriptions.get(format_type, "Unknown")}


def _sv_from_sql(data_dir, **kwargs) -> Dict[str, Any]:
    sql = kwargs.get("sql")
    if not sql:
        return {"error": "sql is required for 'from_sql'"}
    translator = _ensure_translator()
    result = translator.translate(
        sql,
        name=kwargs.get("name") or None,
        database=kwargs.get("database") or None,
        schema_name=kwargs.get("schema_name") or None,
    )
    sv = result.semantic_view
    return {
        "status": "success",
        "input_format": result.input_format.value,
        "semantic_view": {
            "name": sv.name, "database": sv.database,
            "schema": sv.schema_name, "comment": sv.comment,
            "ai_sql_generation": sv.ai_sql_generation,
        },
        "tables": [{"alias": t.alias, "fully_qualified_name": t.fully_qualified_name, "primary_key": t.primary_key} for t in sv.tables],
        "relationships": [
            {"from": f"{r.from_table}.{r.from_column}", "to": f"{r.to_table}" + (f".{r.to_column}" if r.to_column else "")}
            for r in sv.relationships
        ],
        "dimensions": [{"name": d.name, "data_type": d.data_type, "table_alias": d.table_alias, "synonyms": d.synonyms} for d in sv.dimensions],
        "metrics": [{"name": m.name, "data_type": m.data_type, "expression": m.expression, "table_alias": m.table_alias} for m in sv.metrics],
        "facts": [{"name": f.name, "data_type": f.data_type, "table_alias": f.table_alias} for f in sv.facts],
        "warnings": result.warnings,
    }


def _sv_to_project(data_dir, **kwargs) -> Dict[str, Any]:
    sql = kwargs.get("sql")
    project_name = kwargs.get("project_name")
    if not sql or not project_name:
        return {"error": "sql and project_name are required for 'to_project'"}
    service = _ensure_service(data_dir)
    translator = _ensure_translator()
    project = translator.translate_to_project(
        sql=sql, project_name=project_name, service=service,
        description=kwargs.get("description", ""),
        faux_type=kwargs.get("faux_type") or None,
        target_database=kwargs.get("target_database") or None,
        target_schema=kwargs.get("target_schema") or None,
    )
    result = {
        "status": "success",
        "project": {"id": project.id, "name": project.name, "description": project.description},
    }
    if project.semantic_view:
        sv = project.semantic_view
        result["semantic_view"] = {
            "name": sv.fully_qualified_name, "tables": len(sv.tables),
            "dimensions": len(sv.dimensions), "metrics": len(sv.metrics),
            "facts": len(sv.facts), "relationships": len(sv.relationships),
        }
    if project.faux_objects:
        result["faux_objects"] = [
            {"name": o.fully_qualified_name, "type": o.faux_type.value}
            for o in project.faux_objects
        ]
    result["next_steps"] = [
        "Use faux_project(action='get', ...) to view full details",
        "Use faux_project(action='generate_scripts', ...) to generate SQL",
        "Use faux_project(action='add_object', ...) to add more wrapper objects",
    ]
    return result


def _sv_convert(data_dir, **kwargs) -> Dict[str, Any]:
    sql = kwargs.get("sql")
    target_format = kwargs.get("target_format")
    if not sql or not target_format:
        return {"error": "sql and target_format are required for 'convert'"}
    translator = _ensure_translator()
    source_format = translator.detect_format(sql)
    converted = translator.convert(
        sql=sql, target_format=target_format,
        name=kwargs.get("name") or None,
        database=kwargs.get("database") or None,
        schema_name=kwargs.get("schema_name") or None,
        target_database=kwargs.get("target_database") or None,
        target_schema=kwargs.get("target_schema") or None,
    )
    return {
        "status": "success",
        "source_format": source_format.value, "target_format": target_format,
        "sql": converted, "sql_length": len(converted),
    }


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

_PROJECT_ACTIONS = {
    "create": _fp_create,
    "list": _fp_list,
    "get": _fp_get,
    "delete": _fp_delete,
    "add_object": _fp_add_object,
    "remove_object": _fp_remove_object,
    "generate_scripts": _fp_generate_scripts,
    "generate_bundle": _fp_generate_bundle,
    "generate_ddl": _fp_generate_ddl,
    "export": _fp_export,
}

_SV_ACTIONS = {
    "define": _sv_define,
    "add_table": _sv_add_table,
    "add_column": _sv_add_column,
    "add_relationship": _sv_add_relationship,
    "detect_sql": _sv_detect_sql,
    "from_sql": _sv_from_sql,
    "to_project": _sv_to_project,
    "convert": _sv_convert,
}


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

def dispatch_faux_project(data_dir, action: str, **kwargs) -> Dict[str, Any]:
    """Dispatch a faux_project action."""
    handler = _PROJECT_ACTIONS.get(action)
    if not handler:
        return {
            "error": f"Unknown action: '{action}'",
            "valid_actions": sorted(_PROJECT_ACTIONS.keys()),
        }
    try:
        return handler(data_dir, **kwargs)
    except Exception as e:
        logger.error(f"faux_project({action}) failed: {e}")
        return {"error": f"faux_project({action}) failed: {e}"}


def dispatch_semantic_view(data_dir, action: str, **kwargs) -> Dict[str, Any]:
    """Dispatch a semantic_view action."""
    handler = _SV_ACTIONS.get(action)
    if not handler:
        return {
            "error": f"Unknown action: '{action}'",
            "valid_actions": sorted(_SV_ACTIONS.keys()),
        }
    try:
        return handler(data_dir, **kwargs)
    except Exception as e:
        logger.error(f"semantic_view({action}) failed: {e}")
        return {"error": f"semantic_view({action}) failed: {e}"}


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

def register_unified_faux_tools(mcp, data_dir: str):
    """Register the 2 unified faux MCP tools. Returns service instance."""

    @mcp.tool()
    def faux_project(
        action: str,
        project_id: Optional[str] = None,
        name: Optional[str] = None,
        description: Optional[str] = None,
        faux_type: Optional[str] = None,
        target_database: Optional[str] = None,
        target_schema: Optional[str] = None,
        selected_dimensions: Optional[str] = None,
        selected_metrics: Optional[str] = None,
        selected_facts: Optional[str] = None,
        parameters: Optional[str] = None,
        warehouse: Optional[str] = None,
        target_lag: Optional[str] = None,
        schedule: Optional[str] = None,
        materialized_table: Optional[str] = None,
        where_clause: Optional[str] = None,
        comment: Optional[str] = None,
        object_name: Optional[str] = None,
        output_dir: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Unified faux objects project management tool. Replaces 10 individual tools.

        Actions:
        - create: Create a new faux objects project (requires name)
        - list: List all projects
        - get: Get project details (requires project_id)
        - delete: Delete a project (requires project_id)
        - add_object: Add a faux object (requires project_id, name, faux_type, target_database, target_schema)
        - remove_object: Remove a faux object (requires project_id, object_name)
        - generate_scripts: Generate SQL scripts (requires project_id)
        - generate_bundle: Generate deployment bundle (requires project_id)
        - generate_ddl: Generate semantic view DDL (requires project_id)
        - export: Export scripts to files (requires project_id)

        Returns:
            Action-specific result dict
        """
        return dispatch_faux_project(data_dir, action, **{
            k: v for k, v in {
                "project_id": project_id, "name": name, "description": description,
                "faux_type": faux_type, "target_database": target_database,
                "target_schema": target_schema, "selected_dimensions": selected_dimensions,
                "selected_metrics": selected_metrics, "selected_facts": selected_facts,
                "parameters": parameters, "warehouse": warehouse, "target_lag": target_lag,
                "schedule": schedule, "materialized_table": materialized_table,
                "where_clause": where_clause, "comment": comment,
                "object_name": object_name, "output_dir": output_dir,
            }.items() if v is not None
        })

    @mcp.tool()
    def semantic_view(
        action: str,
        project_id: Optional[str] = None,
        name: Optional[str] = None,
        database: Optional[str] = None,
        schema_name: Optional[str] = None,
        comment: Optional[str] = None,
        ai_sql_generation: Optional[str] = None,
        alias: Optional[str] = None,
        fully_qualified_name: Optional[str] = None,
        primary_key: Optional[str] = None,
        column_type: Optional[str] = None,
        data_type: Optional[str] = None,
        table_alias: Optional[str] = None,
        expression: Optional[str] = None,
        synonyms: Optional[str] = None,
        from_table: Optional[str] = None,
        from_column: Optional[str] = None,
        to_table: Optional[str] = None,
        to_column: Optional[str] = None,
        sql: Optional[str] = None,
        target_format: Optional[str] = None,
        project_name: Optional[str] = None,
        description: Optional[str] = None,
        faux_type: Optional[str] = None,
        target_database: Optional[str] = None,
        target_schema: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Unified semantic view management and SQL translation tool. Replaces 8 individual tools.

        Actions:
        - define: Define a semantic view (requires project_id, name, database, schema_name)
        - add_table: Add a table to semantic view (requires project_id, alias, fully_qualified_name)
        - add_column: Add a column (requires project_id, name, column_type)
        - add_relationship: Add a relationship (requires project_id, from_table, from_column, to_table)
        - detect_sql: Detect SQL format (requires sql)
        - from_sql: Parse SQL into semantic view (requires sql)
        - to_project: Parse SQL and create project (requires sql, project_name)
        - convert: Convert SQL between formats (requires sql, target_format)

        Returns:
            Action-specific result dict
        """
        return dispatch_semantic_view(data_dir, action, **{
            k: v for k, v in {
                "project_id": project_id, "name": name, "database": database,
                "schema_name": schema_name, "comment": comment,
                "ai_sql_generation": ai_sql_generation, "alias": alias,
                "fully_qualified_name": fully_qualified_name, "primary_key": primary_key,
                "column_type": column_type, "data_type": data_type,
                "table_alias": table_alias, "expression": expression, "synonyms": synonyms,
                "from_table": from_table, "from_column": from_column,
                "to_table": to_table, "to_column": to_column,
                "sql": sql, "target_format": target_format,
                "project_name": project_name, "description": description,
                "faux_type": faux_type, "target_database": target_database,
                "target_schema": target_schema,
            }.items() if v is not None
        })

    logger.info("Registered 2 unified faux tools: faux_project, semantic_view")
    # Return service instance (preserves existing contract)
    return _ensure_service(data_dir)
