"""DICOM sequence validation and series analysis tools."""

import asyncio
import json
from pathlib import Path
from typing import Any, Dict, Optional

from pydicom import datadict
from mcp.types import ToolAnnotations

from dicom_mcp.server import mcp
from dicom_mcp.config import MAX_FILES
from dicom_mcp.constants import ResponseFormat
from dicom_mcp.helpers.tags import _safe_get_tag, _format_markdown_table
from dicom_mcp.helpers.sequence import _identify_sequence_type
from dicom_mcp.helpers.files import _find_dicom_files


@mcp.tool(
    name="dicom_validate_sequence",
    annotations=ToolAnnotations(
        title="Validate Sequence Parameters",
        readOnlyHint=True,
        destructiveHint=False,
        idempotentHint=True,
        openWorldHint=False,
    ),
)
async def dicom_validate_sequence(
    file_path: str,
    expected_parameters: Optional[Dict[str, Any]] = None,
    manufacturer: Optional[str] = None,
    response_format: ResponseFormat = ResponseFormat.MARKDOWN,
) -> str:
    """Validate MRI sequence parameters against expected values.

    Validates acquisition parameters (TR, TE, flip angle, etc.) against
    expected values. Useful for verifying that test data matches protocol
    specifications or that sequences are consistent across manufacturers.
    """
    try:
        fp = Path(file_path)
        if not fp.exists():
            return f"Error: File not found: {file_path}"

        import pydicom

        ds = await asyncio.to_thread(pydicom.dcmread, fp, stop_before_pixels=True)

        sequence_type = _identify_sequence_type(ds)
        actual_manufacturer = _safe_get_tag(ds, (0x0008, 0x0070))

        validation_results = {
            "file_path": str(fp),
            "sequence_type": sequence_type.value,
            "manufacturer": actual_manufacturer,
            "parameters": {},
            "validation_passed": True,
        }

        if manufacturer:
            manufacturer_match = manufacturer.lower() in actual_manufacturer.lower()
            validation_results["manufacturer_match"] = manufacturer_match
            if not manufacturer_match:
                validation_results["validation_passed"] = False

        if expected_parameters:
            param_tag_map = {
                "RepetitionTime": (0x0018, 0x0080),
                "EchoTime": (0x0018, 0x0081),
                "InversionTime": (0x0018, 0x0082),
                "FlipAngle": (0x0018, 0x1314),
                "ScanningSequence": (0x0018, 0x0020),
                "SequenceVariant": (0x0018, 0x0021),
                "MRAcquisitionType": (0x0018, 0x0023),
            }

            for param_name, expected_value in expected_parameters.items():
                if param_name in param_tag_map:
                    tag = param_tag_map[param_name]
                    actual_value = _safe_get_tag(ds, tag)
                    match = str(actual_value) == str(expected_value)
                    validation_results["parameters"][param_name] = {
                        "actual": actual_value,
                        "expected": str(expected_value),
                        "match": match,
                    }
                    if not match:
                        validation_results["validation_passed"] = False

        if response_format == ResponseFormat.JSON:
            return json.dumps(validation_results, indent=2)
        else:
            output = [f"# Sequence Validation: {fp.name}\n"]
            status = "PASSED" if validation_results["validation_passed"] else "FAILED"
            output.append(f"**Validation Status**: {status}\n")
            output.append("## Sequence Information")
            output.append(f"- **Sequence Type**: {sequence_type.value}")
            output.append(f"- **Manufacturer**: {actual_manufacturer}")
            if manufacturer:
                match_status = (
                    "Match"
                    if validation_results.get("manufacturer_match", True)
                    else "Mismatch"
                )
                output.append(
                    f"- **Expected Manufacturer**: {manufacturer} ({match_status})"
                )
            output.append("")
            if validation_results["parameters"]:
                output.append("## Parameter Validation\n")
                headers = ["Parameter", "Actual", "Expected", "Status"]
                rows = []
                for pname, data in validation_results["parameters"].items():
                    s = "Match" if data["match"] else "Mismatch"
                    rows.append([pname, data["actual"], data["expected"], s])
                output.append(_format_markdown_table(headers, rows))
            return "\n".join(output)

    except Exception as e:
        return f"Error validating sequence: {str(e)}"


@mcp.tool(
    name="dicom_analyze_series",
    annotations=ToolAnnotations(
        title="Analyze DICOM Series",
        readOnlyHint=True,
        destructiveHint=False,
        idempotentHint=True,
        openWorldHint=False,
    ),
)
async def dicom_analyze_series(
    directory: str,
    response_format: ResponseFormat = ResponseFormat.MARKDOWN,
) -> str:
    """Analyze a complete DICOM series for consistency and completeness.

    Analyzes all files in a series directory, checking for consistency of
    acquisition parameters, completeness of the series, and identifying
    any anomalies. Useful for validating test datasets.
    """
    try:
        dir_path = Path(directory)
        if not dir_path.exists():
            return f"Error: Directory not found: {directory}"

        dicom_files, truncated = await asyncio.to_thread(
            _find_dicom_files, dir_path, False
        )

        if not dicom_files:
            return f"No DICOM files found in {directory}"

        first_fp, first_ds = dicom_files[0]
        series_info = {
            "series_description": _safe_get_tag(first_ds, (0x0008, 0x103E)),
            "series_number": _safe_get_tag(first_ds, (0x0020, 0x0011)),
            "series_uid": _safe_get_tag(first_ds, (0x0020, 0x000E)),
            "modality": _safe_get_tag(first_ds, (0x0008, 0x0060)),
            "manufacturer": _safe_get_tag(first_ds, (0x0008, 0x0070)),
            "sequence_type": _identify_sequence_type(first_ds).value,
        }

        consistency_issues = []
        params_to_check = [
            (0x0018, 0x0080),  # RepetitionTime
            (0x0018, 0x0081),  # EchoTime
            (0x0020, 0x000E),  # SeriesInstanceUID
            (0x0028, 0x0010),  # Rows
            (0x0028, 0x0011),  # Columns
        ]

        for tag in params_to_check:
            tag_name = datadict.keyword_for_tag(tag)
            values = set(_safe_get_tag(ds, tag) for _, ds in dicom_files)
            if len(values) > 1:
                consistency_issues.append(
                    f"{tag_name} varies across series: {', '.join(str(v) for v in values)}"
                )

        instance_numbers = []
        for _, ds in dicom_files:
            try:
                inst_num = int(_safe_get_tag(ds, (0x0020, 0x0013)))
                instance_numbers.append(inst_num)
            except (ValueError, TypeError):
                pass

        if instance_numbers:
            instance_numbers.sort()
            expected_instances = max(instance_numbers)
            expected_set = set(range(1, expected_instances + 1))
            found_set = set(instance_numbers)
            missing = sorted(list(expected_set - found_set))
            complete = len(missing) == 0
        else:
            expected_instances = 0
            missing = []
            complete = False
            consistency_issues.append("Could not determine instance numbers")

        result = {
            "series_info": series_info,
            "file_count": len(dicom_files),
            "truncated": truncated,
            "consistency_check": {
                "consistent_parameters": len(consistency_issues) == 0,
                "issues": consistency_issues,
            },
            "completeness": {
                "expected_instances": expected_instances,
                "found_instances": len(instance_numbers),
                "missing": missing,
                "complete": complete,
            },
        }

        if response_format == ResponseFormat.JSON:
            return json.dumps(result, indent=2)
        else:
            output = [f"# Series Analysis: {dir_path.name}\n", "## Series Information"]
            for key, value in series_info.items():
                output.append(f"- **{key.replace('_', ' ').title()}**: {value}")
            output.append(f"- **Total Files**: {len(dicom_files)}")
            if truncated:
                output.append(f"- **Warning**: Scan truncated at {MAX_FILES} files")
            output.append("")
            output.append("## Consistency Check")
            if result["consistency_check"]["consistent_parameters"]:
                output.append("All parameters are consistent across the series")
            else:
                output.append("Inconsistencies detected:")
                for issue in result["consistency_check"]["issues"]:
                    output.append(f"  - {issue}")
            output.append("")
            output.append("## Completeness Check")
            if result["completeness"]["complete"]:
                output.append(f"Series is complete ({expected_instances} instances)")
            else:
                output.append("Series may be incomplete")
                output.append(
                    f"- Expected: {result['completeness']['expected_instances']} instances"
                )
                output.append(
                    f"- Found: {result['completeness']['found_instances']} instances"
                )
                if missing:
                    output.append(f"- Missing: {', '.join(str(m) for m in missing)}")
            return "\n".join(output)

    except Exception as e:
        return f"Error analyzing series: {str(e)}"
