"""
Add direct load step tool for the AGS SPARQL Agent.

This tool adds a DirectLoadStep that loads JSON files using the DataToolkit
service, automatically generating RDF from JSON data based on an ontology.
"""

import os
import sys
import uuid
from datetime import datetime

# Add src directory to path for imports
src_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
if src_dir not in sys.path:
    sys.path.insert(0, src_dir)

from ..base_tool import BaseToolMixin, handle_tool_errors, ToolError
from ...utils.agent_utils import get_agent, get_graphmart_uri


@handle_tool_errors
async def add_direct_load_step(
    layer_name_or_uri: str,
    step_name: str,
    file_path: str,
    model_name: str,
    ontology_namespace: str,
    base_uri: str,
    explanation: str,
    file_pattern: str = "*.json",
    graphmart_uri: str = None,
    dry_run: bool = False,
    concurrency_limit: int = None,
    concurrency_nodes: int = None,
    concurrency_executors_per_node: int = None,
    batching: int = None,
    errors: bool = None
) -> str:
    """Create a new direct load step within a transformation layer.
    
    This tool adds a DirectLoadStep that loads JSON files using the DataToolkit
    service, automatically generating RDF from JSON data based on an ontology.
    
    Args:
        layer_name_or_uri: Layer name or full URI to add step to
        step_name: Human-readable name for the step
        file_path: Directory path containing the JSON files
        model_name: Model name for the data (e.g., "Customer", "PurchaseOrder")
        ontology_namespace: Full ontology URI (e.g., "http://disw.siemens.com/ontologies/CRM")
        base_uri: Base URI for generated entities (e.g., "http://siemens.com/CRM")
        explanation: Human-readable description of the operation (required)
        file_pattern: File pattern to match (default: "*.json")
        graphmart_uri: URI of the graphmart (optional, auto-detects)
        dry_run: If true, validate inputs without executing
        concurrency_limit: Total concurrency limit (optional, overrides default)
        concurrency_nodes: Number of nodes (optional, overrides default)
        concurrency_executors_per_node: Executors per node (optional, overrides default)
        batching: Batch size (optional, overrides default)
        errors: Error handling flag (optional, overrides default)
        
    Returns:
        JSON response with direct load step creation results
    """
    BaseToolMixin.validate_required_param(layer_name_or_uri, "layer_name_or_uri")
    BaseToolMixin.validate_required_param(step_name, "step_name")
    BaseToolMixin.validate_required_param(file_path, "file_path")
    BaseToolMixin.validate_required_param(model_name, "model_name")
    BaseToolMixin.validate_required_param(ontology_namespace, "ontology_namespace")
    BaseToolMixin.validate_required_param(base_uri, "base_uri")
    BaseToolMixin.validate_required_param(explanation, "explanation")
    
    # Auto-detect graphmart URI if not provided
    graphmart_uri = get_graphmart_uri(graphmart_uri)
    
    agent = await get_agent()
    
    # Resolve layer URI from name if needed
    layer_uri = None
    if layer_name_or_uri.startswith("http://"):
        layer_uri = layer_name_or_uri
    else:
        # Find layer by name
        layer_lookup_query = f"""
        PREFIX gm: <http://cambridgesemantics.com/ontologies/Graphmarts#>
        PREFIX anzo: <http://openanzo.org/ontologies/2008/07/Anzo#>
        PREFIX dc: <http://purl.org/dc/elements/1.1/>
        
        SELECT ?layer WHERE {{
            GRAPH <{graphmart_uri}> {{
                <{graphmart_uri}> gm:layer ?orderedItem .
                ?orderedItem anzo:orderedValue ?layer .
                ?layer dc:title "{layer_name_or_uri}" .
            }}
        }}
        """
        lookup_result = agent.anzo_client.query_journal(layer_lookup_query)
        table_results = lookup_result.as_table_results().as_list()
        
        if not table_results or len(table_results) == 0:
            raise ToolError(f"Layer '{layer_name_or_uri}' not found in graphmart")
        
        layer_uri = table_results[0][0]
    
    # Generate unique URIs for the direct load step and ordered item
    step_uuid = str(uuid.uuid4()).replace('-', '')
    ordered_item_uuid = str(uuid.uuid4()).replace('-', '')
    
    step_uri = f"http://cambridgesemantics.com/DirectLoadStep/{step_uuid}"
    ordered_item_uri = f"http://cambridgesemantics.com/OrderedItem/{ordered_item_uuid}"
    
    # Build concurrency configuration
    if concurrency_limit is not None and concurrency_nodes is not None and concurrency_executors_per_node is not None:
        concurrency_config = f"s:concurrency [ s:limit {concurrency_limit} ; s:nodes {concurrency_nodes} ; s:executorsPerNode {concurrency_executors_per_node} ; ] ;"
    else:
        concurrency_config = "s:concurrency 1 ;"
    
    # Build optional settings
    batching_config = f"s:batching {batching} ;" if batching is not None else ""
    errors_config = f"s:errors {str(errors).lower()} ;" if errors is not None else ""
    
    # Create the DataToolkit transform query for direct loading
    transform_query = f"""PREFIX s:  <http://cambridgesemantics.com/ontologies/DataToolkit#>
PREFIX rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX xsd:  <http://www.w3.org/2001/XMLSchema#>
PREFIX owl:  <http://www.w3.org/2002/07/owl#>
PREFIX anzo: <http://openanzo.org/ontologies/2008/07/Anzo#>
PREFIX zowl: <http://openanzo.org/ontologies/2009/05/AnzoOwl#>
PREFIX dc:  <http://purl.org/dc/elements/1.1/>

INSERT
{{
 GRAPH ${{targetGraph}}
 {{
  ?subject ?predicate ?object .
 }}
}}
WHERE
{{
 SERVICE <http://cambridgesemantics.com/services/DataToolkit>
 {{
  ?data a s:FileSource ;
   s:url "{file_path}" ;
   s:pattern "{file_pattern}" ;
   s:model "{model_name}" ;
   .

  ?generator a s:OntologyGenerator , s:RdfGenerator ;
   {concurrency_config}
   {batching_config}
   {errors_config}
   s:as (?subject ?predicate ?object ?graph) ;
   s:ontology <{ontology_namespace}> ;
   s:base <{base_uri}> ;
   .
 }}
}}"""
    
    # Dry run mode
    if dry_run:
        return BaseToolMixin.create_success_response({
            "dry_run": True,
            "layer_name_or_uri": layer_name_or_uri,
            "resolved_layer_uri": layer_uri,
            "step_name": step_name,
            "step_uri": step_uri,
            "ordered_item_uri": ordered_item_uri,
            "file_path": file_path,
            "model_name": model_name,
            "ontology_namespace": ontology_namespace,
            "base_uri": base_uri,
            "graphmart_uri": graphmart_uri,
            "explanation": explanation,
            "transform_query": transform_query
        }, "Direct load step creation validated. Set dry_run=false to execute.")
    
    # Get current timestamp for metadata
    current_timestamp = datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
    
    # Get current highest step index within the target layer
    try:
        step_index_query = f"""
        PREFIX anzo: <http://openanzo.org/ontologies/2008/07/Anzo#>
        PREFIX gm: <http://cambridgesemantics.com/ontologies/Graphmarts#>
        
        SELECT ?index WHERE {{
            GRAPH <{graphmart_uri}> {{
                <{layer_uri}> gm:step ?orderedItem .
                ?orderedItem anzo:index ?index .
            }}
        }}
        """
        step_index_result = agent.anzo_client.query_journal(step_index_query)
        
        # Parse all step indices and find the maximum numerically
        next_step_index = 0
        table_results = step_index_result.as_table_results().as_list()
        
        if table_results:
            indices = []
            for result_row in table_results:
                if result_row and len(result_row) > 0:
                    index_value = result_row[0]
                    try:
                        indices.append(int(index_value))
                    except (ValueError, TypeError):
                        BaseToolMixin.log_warning(f"Skipping non-numeric step index: {index_value}")
            
            if indices:
                next_step_index = max(indices) + 1
                
    except Exception as index_error:
        BaseToolMixin.log_warning(f"Step index query failed: {index_error}")
        next_step_index = 0
    
    # Escape the SPARQL query for insertion
    escaped_query = transform_query.replace('"""', '\\"\\"\\"')
    
    # Create the direct load step and its metadata
    step_creation_sparql = f"""
    PREFIX gm: <http://cambridgesemantics.com/ontologies/Graphmarts#>
    PREFIX dc: <http://purl.org/dc/elements/1.1/>
    PREFIX dct: <http://purl.org/dc/terms/>
    PREFIX anzo: <http://openanzo.org/ontologies/2008/07/Anzo#>
    
    INSERT DATA {{
        GRAPH <{graphmart_uri}> {{
            <{step_uri}> a gm:Step, gm:LayerChild, gm:TransformStep, gm:DirectLoadStep ;
                        dc:title "{step_name}" ;
                        gm:transformQuery '''{escaped_query}''' ;
                        gm:enabled "true"^^<http://www.w3.org/2001/XMLSchema#boolean> ;
                        gm:preview "false"^^<http://www.w3.org/2001/XMLSchema#boolean> ;
                        gm:disableLoadCounts "false"^^<http://www.w3.org/2001/XMLSchema#boolean> ;
                        gm:preGenerateStatistics "false"^^<http://www.w3.org/2001/XMLSchema#boolean> ;
                        gm:preVacuum "false"^^<http://www.w3.org/2001/XMLSchema#boolean> ;
                        gm:ontology gm:Managed ;
                        gm:source gm:AllPrevious, gm:Self ;
                        dct:created "{current_timestamp}" ;
                        dct:creator <http://openanzo.org/system/internal/sysadmin> ;
                        dct:modified "{current_timestamp}" ;
                        dct:modifier <http://openanzo.org/system/internal/sysadmin> .
            
            <{ordered_item_uri}> a anzo:IndexedItem, anzo:OrderedValue ;
                                anzo:orderedValue <{step_uri}> ;
                                anzo:index "{next_step_index}"^^<http://www.w3.org/2001/XMLSchema#int> ;
                                dct:created "{current_timestamp}" ;
                                dct:creator <http://openanzo.org/system/internal/sysadmin> ;
                                dct:modified "{current_timestamp}" ;
                                dct:modifier <http://openanzo.org/system/internal/sysadmin> .
            
            <{layer_uri}> gm:step <{ordered_item_uri}> .
        }}
    }}
    """
    
    # Execute the direct load step creation
    agent.anzo_client.update_journal(step_creation_sparql)
    
    return BaseToolMixin.create_success_response({
        "step_name": step_name,
        "step_uri": step_uri,
        "ordered_item_uri": ordered_item_uri,
        "step_index": next_step_index,
        "layer_uri": layer_uri,
        "layer_name_or_uri": layer_name_or_uri,
        "file_path": file_path,
        "model_name": model_name,
        "ontology_namespace": ontology_namespace,
        "base_uri": base_uri,
        "graphmart_uri": graphmart_uri,
        "explanation": explanation,
        "transform_query": transform_query
    }, f"Successfully created direct load step '{step_name}' in layer")