"""
Test SHACL rules against sample instances without writing to Records API.

This handler allows testing rules in the React UI before deployment.
Results are returned directly without persisting to CDF Records.
"""

from typing import Any

from common.shacl_utils import load_shacl_from_file


def fetch_test_instances(
    client, instance_selection: dict[str, Any], datamodel_space: str, datamodel_external_id: str, datamodel_version: str
) -> list[dict]:
    """
    Fetch instances based on selection method.

    Args:
        client: Cognite client
        instance_selection: Dict with:
            - method: "filter" | "explicit" | "random"
            - filter: DMS filter (for filter method)
            - instance_ids: List of (space, external_id) tuples (for explicit method)
            - limit: Max instances to fetch
        datamodel_space: Data model space
        datamodel_external_id: Data model external ID
        datamodel_version: Data model version

    Returns:
        List of instance dictionaries
    """
    from cognite.client.data_classes.data_modeling import ViewId

    method = instance_selection.get("method", "filter")
    limit = instance_selection.get("limit", 100)

    view_id = ViewId(datamodel_space, datamodel_external_id, datamodel_version)

    if method == "explicit":
        # Fetch specific instances by ID
        instance_ids = instance_selection.get("instance_ids", [])

        # Convert list of strings to list of (space, external_id) tuples
        # Expected format: ["space/external_id", "space/external_id"]
        node_ids = []
        for inst_id in instance_ids[:limit]:
            if "/" in inst_id:
                space, ext_id = inst_id.split("/", 1)
                node_ids.append((space, ext_id))
            else:
                # Assume same space as data model
                node_ids.append((datamodel_space, inst_id))

        if not node_ids:
            return []

        result = client.data_modeling.instances.retrieve(nodes=node_ids, sources=[view_id])

        return [node.dump() for node in result.nodes]

    elif method == "filter":
        # Fetch instances using DMS filter
        filter_dict = instance_selection.get("filter")

        result = client.data_modeling.instances.list(
            instance_type="node", sources=[view_id], filter=filter_dict, limit=limit
        )

        return [node.dump() for node in result.data]

    elif method == "random":
        # Fetch random sample (just use list with no filter)
        result = client.data_modeling.instances.list(instance_type="node", sources=[view_id], limit=limit)

        return [node.dump() for node in result.data]

    else:
        raise ValueError(f"Unknown instance selection method: {method}")


def parse_violations_from_report(report_graph) -> list[dict]:
    """
    Parse violations from SHACL validation report graph.

    Args:
        report_graph: RDF graph from PyShacl

    Returns:
        List of violation dictionaries
    """
    if not report_graph:
        return []

    from rdflib import RDF, Namespace

    SH = Namespace("http://www.w3.org/ns/shacl#")

    violations = []

    # Find all ValidationResult nodes
    for result in report_graph.subjects(predicate=RDF.type, object=SH.ValidationResult):
        violation = {}

        # Extract all properties
        for pred, obj in report_graph.predicate_objects(subject=result):
            pred_name = str(pred).split("#")[-1]
            violation[pred_name] = str(obj)

        violations.append(violation)

    return violations


def handle_test_rule(data: dict[str, Any], client) -> dict[str, Any]:
    """
    Test SHACL rules against sample instances.

    This handler is specifically for the React UI to test rules before deployment.
    Results are NOT written to Records API.

    Args:
        data: Input data containing:
            - shacl_rules: SHACL rules as Turtle string (or use shacl_rules_file_external_id)
            - shacl_rules_file_external_id: (optional) External ID of CDF File with rules
            - datamodel_space: Space of the data model
            - datamodel_external_id: External ID of the data model
            - datamodel_version: Version of the data model
            - instance_selection: Dict with:
                - method: "filter" | "explicit" | "random"
                - filter: DMS filter (for filter method)
                - instance_ids: List of instance IDs (for explicit method)
                - limit: Max instances to fetch (default: 100)
            - auto_load_depth: (optional) Maximum depth for auto-loading (default: 2)
        client: Cognite client instance

    Returns:
        Dict with test results:
            - success: Boolean indicating if test completed
            - total_instances: Number of instances tested
            - passed: Number of instances that passed
            - failed: Number of instances that failed
            - violations: List of violation details
            - execution_time: Time taken in seconds
            - report_text: Human-readable report (optional)
    """
    import time

    from cognite_data_quality._neat import NeatSession

    print("=" * 80)
    print("SHACL RULE TEST (NO RECORDS POSTING)")
    print("=" * 80)

    start_time = time.time()

    # Extract parameters
    shacl_rules = data.get("shacl_rules")
    shacl_rules_file_external_id = data.get("shacl_rules_file_external_id")
    datamodel_space = data.get("datamodel_space")
    datamodel_external_id = data.get("datamodel_external_id")
    datamodel_version = data.get("datamodel_version")
    instance_selection = data.get("instance_selection", {})
    auto_load_depth = data.get("auto_load_depth", 2)

    # Load SHACL rules from file if needed
    if shacl_rules_file_external_id and not shacl_rules:
        try:
            print(f"Loading SHACL rules from CDF File: {shacl_rules_file_external_id}")
            shacl_rules = load_shacl_from_file(client, shacl_rules_file_external_id)
            print(f"  Loaded {len(shacl_rules)} characters")
        except Exception as e:
            return {
                "success": False,
                "error": f"Failed to load SHACL rules: {e!s}",
                "total_instances": 0,
                "passed": 0,
                "failed": 0,
                "violations": [],
                "execution_time": time.time() - start_time,
            }

    # Validate required parameters
    if not shacl_rules:
        return {
            "success": False,
            "error": "Missing required parameter: shacl_rules or shacl_rules_file_external_id",
            "total_instances": 0,
            "passed": 0,
            "failed": 0,
            "violations": [],
            "execution_time": time.time() - start_time,
        }

    if not all([datamodel_space, datamodel_external_id, datamodel_version]):
        return {
            "success": False,
            "error": "Missing required parameters: datamodel_space, datamodel_external_id, datamodel_version",
            "total_instances": 0,
            "passed": 0,
            "failed": 0,
            "violations": [],
            "execution_time": time.time() - start_time,
        }

    # Fetch test instances
    try:
        print("Fetching test instances...")
        print(f"  Method: {instance_selection.get('method', 'filter')}")
        print(f"  Limit: {instance_selection.get('limit', 100)}")

        instances = fetch_test_instances(
            client,
            instance_selection,
            datamodel_space or "",
            datamodel_external_id or "",
            datamodel_version or "",
        )

        print(f"  Fetched {len(instances)} instances")
    except Exception as e:
        return {
            "success": False,
            "error": f"Failed to fetch test instances: {e!s}",
            "total_instances": 0,
            "passed": 0,
            "failed": 0,
            "violations": [],
            "execution_time": time.time() - start_time,
        }

    if not instances:
        return {
            "success": True,
            "message": "No instances found matching selection criteria",
            "total_instances": 0,
            "passed": 0,
            "failed": 0,
            "violations": [],
            "execution_time": time.time() - start_time,
        }

    # Run validation
    print(f"Validating {len(instances)} instances...")
    print(f"Data model: {datamodel_space}/{datamodel_external_id}/{datamodel_version}")
    print(f"Auto-load depth: {auto_load_depth}")

    try:
        neat = NeatSession(client, verbose=False)

        conforms, report_graph, report_text = neat.validate_instances.with_shacl(
            instances=instances,
            shacl_rules=shacl_rules,
            datamodel_space=datamodel_space,
            datamodel_external_id=datamodel_external_id,
            datamodel_version=datamodel_version,
            auto_load_depth=auto_load_depth,
            verbose=True,
        )

        # Parse violations
        violations = parse_violations_from_report(report_graph)

        # Count unique failed instances (by focusNode)
        failed_instances = set()
        for v in violations:
            focus_node = v.get("focusNode")
            if focus_node:
                failed_instances.add(focus_node)

        failed_count = len(failed_instances)
        passed_count = len(instances) - failed_count

        execution_time = time.time() - start_time

        # Print summary
        print("=" * 80)
        print(f"TEST {'PASSED' if conforms else 'FAILED'}")
        print("=" * 80)
        print(f"Total instances: {len(instances)}")
        print(f"Passed: {passed_count}")
        print(f"Failed: {failed_count}")
        print(f"Violations found: {len(violations)}")
        print(f"Execution time: {execution_time:.2f}s")
        print("=" * 80)

        return {
            "success": True,
            "total_instances": len(instances),
            "passed": passed_count,
            "failed": failed_count,
            "violations": violations,
            "execution_time": execution_time,
            "report_text": report_text,
            "conforms": conforms,
        }

    except Exception as e:
        import traceback

        error_trace = traceback.format_exc()
        print("ERROR during validation:")
        print(error_trace)

        return {
            "success": False,
            "error": f"Validation failed: {e!s}",
            "error_trace": error_trace,
            "total_instances": len(instances),
            "passed": 0,
            "failed": 0,
            "violations": [],
            "execution_time": time.time() - start_time,
        }
