"""DM Schema V1/V2 Tool-Specific Processors."""

from typing import Any, Protocol, runtime_checkable

from ..utils import generate_random_id


@runtime_checkable
class ToolProcessor(Protocol):
    """Tool-Specific Conversion Processor Interface."""

    @property
    def tool_name(self) -> str:
        """Tool name (e.g., 'bounding_box', 'polygon')."""
        ...

    def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
        """Convert V1 annotation to V2 format."""
        ...

    def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
        """Convert V2 annotation to V1 format."""
        ...


class BoundingBoxProcessor:
    """Bounding Box Tool Processor."""

    tool_name = 'bounding_box'
    _COORDINATE_ATTRS = {'rotation'}
    _INTERNAL_ATTR_PREFIX = '_'

    def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
        coordinate = v1_data.get('coordinate', {})
        classification_obj = v1_annotation.get('classification') or {}

        data = [
            coordinate.get('x', 0),
            coordinate.get('y', 0),
            coordinate.get('width', 0),
            coordinate.get('height', 0),
        ]

        attrs: list[dict[str, Any]] = []
        if 'rotation' in coordinate:
            attrs.append({'name': 'rotation', 'value': coordinate['rotation']})

        for key, value in classification_obj.items():
            if key != 'class':
                attrs.append({'name': key, 'value': value})

        return {
            'id': v1_annotation.get('id', ''),
            'classification': classification_obj.get('class', ''),
            'attrs': attrs,
            'data': data,
        }

    def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
        annotation_id = v2_annotation.get('id', '')
        classification_str = v2_annotation.get('classification', '')
        attrs = v2_annotation.get('attrs', [])
        data = v2_annotation.get('data', [0, 0, 0, 0])

        coordinate: dict[str, Any] = {
            'x': data[0] if len(data) > 0 else 0,
            'y': data[1] if len(data) > 1 else 0,
            'width': data[2] if len(data) > 2 else 0,
            'height': data[3] if len(data) > 3 else 0,
        }

        classification: dict[str, Any] = {'class': classification_str}

        for attr in attrs:
            name = attr.get('name', '')
            value = attr.get('value')
            if name in self._COORDINATE_ATTRS:
                coordinate[name] = value
            elif not name.startswith(self._INTERNAL_ATTR_PREFIX):
                classification[name] = value

        return (
            {'id': annotation_id, 'tool': self.tool_name, 'classification': classification},
            {'id': annotation_id, 'coordinate': coordinate},
        )


class PolygonProcessor:
    """Polygon Tool Processor."""

    tool_name = 'polygon'
    _INTERNAL_ATTR_PREFIX = '_'

    def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
        coordinate = v1_data.get('coordinate', [])
        classification_obj = v1_annotation.get('classification') or {}

        data = []
        for point in coordinate:
            if isinstance(point, dict):
                data.append([point.get('x', 0), point.get('y', 0)])

        attrs: list[dict[str, Any]] = []
        for key, value in classification_obj.items():
            if key != 'class':
                attrs.append({'name': key, 'value': value})

        return {
            'id': v1_annotation.get('id', ''),
            'classification': classification_obj.get('class', ''),
            'attrs': attrs,
            'data': data,
        }

    def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
        annotation_id = v2_annotation.get('id', '')
        classification_str = v2_annotation.get('classification', '')
        attrs = v2_annotation.get('attrs', [])
        data = v2_annotation.get('data', [])

        coordinate: list[dict[str, Any]] = []
        for point in data:
            if isinstance(point, list) and len(point) >= 2:
                coordinate.append({'x': point[0], 'y': point[1], 'id': generate_random_id()})

        classification: dict[str, Any] = {'class': classification_str}
        for attr in attrs:
            name = attr.get('name', '')
            value = attr.get('value')
            if not name.startswith(self._INTERNAL_ATTR_PREFIX):
                classification[name] = value

        return (
            {'id': annotation_id, 'tool': self.tool_name, 'classification': classification},
            {'id': annotation_id, 'coordinate': coordinate},
        )


class PolylineProcessor:
    """Polyline Tool Processor."""

    tool_name = 'polyline'
    _INTERNAL_ATTR_PREFIX = '_'

    def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
        coordinate = v1_data.get('coordinate', [])
        classification_obj = v1_annotation.get('classification') or {}

        data = []
        for point in coordinate:
            if isinstance(point, dict):
                data.append([point.get('x', 0), point.get('y', 0)])

        attrs: list[dict[str, Any]] = []
        for key, value in classification_obj.items():
            if key != 'class':
                attrs.append({'name': key, 'value': value})

        return {
            'id': v1_annotation.get('id', ''),
            'classification': classification_obj.get('class', ''),
            'attrs': attrs,
            'data': data,
        }

    def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
        annotation_id = v2_annotation.get('id', '')
        classification_str = v2_annotation.get('classification', '')
        attrs = v2_annotation.get('attrs', [])
        data = v2_annotation.get('data', [])

        coordinate: list[dict[str, Any]] = []
        for point in data:
            if isinstance(point, list) and len(point) >= 2:
                coordinate.append({'x': point[0], 'y': point[1], 'id': generate_random_id()})

        classification: dict[str, Any] = {'class': classification_str}
        for attr in attrs:
            name = attr.get('name', '')
            value = attr.get('value')
            if not name.startswith(self._INTERNAL_ATTR_PREFIX):
                classification[name] = value

        return (
            {'id': annotation_id, 'tool': self.tool_name, 'classification': classification},
            {'id': annotation_id, 'coordinate': coordinate},
        )


class KeypointProcessor:
    """Keypoint Tool Processor."""

    tool_name = 'keypoint'
    _INTERNAL_ATTR_PREFIX = '_'

    def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
        coordinate = v1_data.get('coordinate', {})
        classification_obj = v1_annotation.get('classification') or {}

        data = [coordinate.get('x', 0), coordinate.get('y', 0)]

        attrs: list[dict[str, Any]] = []
        for key, value in classification_obj.items():
            if key != 'class':
                attrs.append({'name': key, 'value': value})

        return {
            'id': v1_annotation.get('id', ''),
            'classification': classification_obj.get('class', ''),
            'attrs': attrs,
            'data': data,
        }

    def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
        annotation_id = v2_annotation.get('id', '')
        classification_str = v2_annotation.get('classification', '')
        attrs = v2_annotation.get('attrs', [])
        data = v2_annotation.get('data', [])

        coordinate: dict[str, Any] = {}
        if isinstance(data, list) and len(data) >= 2:
            coordinate = {'x': data[0], 'y': data[1]}

        classification: dict[str, Any] = {'class': classification_str}
        for attr in attrs:
            name = attr.get('name', '')
            value = attr.get('value')
            if not name.startswith(self._INTERNAL_ATTR_PREFIX):
                classification[name] = value

        return (
            {'id': annotation_id, 'tool': self.tool_name, 'classification': classification},
            {'id': annotation_id, 'coordinate': coordinate},
        )


class BoundingBox3DProcessor:
    """3D Bounding Box Tool Processor."""

    tool_name = '3d_bounding_box'
    _INTERNAL_ATTR_PREFIX = '_'

    def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
        psr = v1_data.get('psr', {})
        classification_obj = v1_annotation.get('classification') or {}

        data = {
            'position': psr.get('position', {'x': 0, 'y': 0, 'z': 0}),
            'scale': psr.get('scale', {'x': 0, 'y': 0, 'z': 0}),
            'rotation': psr.get('rotation', {'x': 0, 'y': 0, 'z': 0}),
        }

        attrs: list[dict[str, Any]] = []
        for key, value in classification_obj.items():
            if key != 'class':
                attrs.append({'name': key, 'value': value})

        return {
            'id': v1_annotation.get('id', ''),
            'classification': classification_obj.get('class', ''),
            'attrs': attrs,
            'data': data,
        }

    def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
        annotation_id = v2_annotation.get('id', '')
        classification_str = v2_annotation.get('classification', '')
        attrs = v2_annotation.get('attrs', [])
        data = v2_annotation.get('data', {})

        psr: dict[str, Any] = {
            'position': data.get('position', {'x': 0, 'y': 0, 'z': 0}),
            'scale': data.get('scale', {'x': 0, 'y': 0, 'z': 0}),
            'rotation': data.get('rotation', {'x': 0, 'y': 0, 'z': 0}),
        }

        classification: dict[str, Any] = {'class': classification_str}
        for attr in attrs:
            name = attr.get('name', '')
            value = attr.get('value')
            if not name.startswith(self._INTERNAL_ATTR_PREFIX):
                classification[name] = value

        return (
            {'id': annotation_id, 'tool': self.tool_name, 'classification': classification},
            {'id': annotation_id, 'psr': psr},
        )


class SegmentationProcessor:
    """Segmentation Tool Processor (Image/Video Unified)."""

    tool_name = 'segmentation'
    _INTERNAL_ATTR_PREFIX = '_'

    def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
        classification_obj = v1_annotation.get('classification') or {}

        if 'pixel_indices' in v1_data:
            data = v1_data.get('pixel_indices', [])
        elif 'section' in v1_data:
            section = v1_data.get('section', {})
            data = {'startFrame': section.get('startFrame', 0), 'endFrame': section.get('endFrame', 0)}
        else:
            data = []

        attrs: list[dict[str, Any]] = []
        for key, value in classification_obj.items():
            if key != 'class':
                attrs.append({'name': key, 'value': value})

        return {
            'id': v1_annotation.get('id', ''),
            'classification': classification_obj.get('class', ''),
            'attrs': attrs,
            'data': data,
        }

    def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
        annotation_id = v2_annotation.get('id', '')
        classification_str = v2_annotation.get('classification', '')
        attrs = v2_annotation.get('attrs', [])
        data = v2_annotation.get('data', [])

        classification: dict[str, Any] = {'class': classification_str}
        for attr in attrs:
            name = attr.get('name', '')
            value = attr.get('value')
            if not name.startswith(self._INTERNAL_ATTR_PREFIX):
                classification[name] = value

        v1_data: dict[str, Any] = {'id': annotation_id}
        if isinstance(data, list):
            v1_data['pixel_indices'] = data
        elif isinstance(data, dict):
            v1_data['section'] = {'startFrame': data.get('startFrame', 0), 'endFrame': data.get('endFrame', 0)}
        else:
            v1_data['pixel_indices'] = []

        return (
            {'id': annotation_id, 'tool': self.tool_name, 'classification': classification},
            v1_data,
        )


class Segmentation3DProcessor:
    """3D Segmentation Tool Processor."""

    tool_name = '3d_segmentation'
    _INTERNAL_ATTR_PREFIX = '_'

    def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
        classification_obj = v1_annotation.get('classification') or {}
        data = {'points': v1_data.get('points', [])}

        attrs: list[dict[str, Any]] = []
        for key, value in classification_obj.items():
            if key != 'class':
                attrs.append({'name': key, 'value': value})

        return {
            'id': v1_annotation.get('id', ''),
            'classification': classification_obj.get('class', ''),
            'attrs': attrs,
            'data': data,
        }

    def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
        annotation_id = v2_annotation.get('id', '')
        classification_str = v2_annotation.get('classification', '')
        attrs = v2_annotation.get('attrs', [])
        data = v2_annotation.get('data', {})

        classification: dict[str, Any] = {'class': classification_str}
        for attr in attrs:
            name = attr.get('name', '')
            value = attr.get('value')
            if not name.startswith(self._INTERNAL_ATTR_PREFIX):
                classification[name] = value

        return (
            {'id': annotation_id, 'tool': self.tool_name, 'classification': classification},
            {'id': annotation_id, 'points': data.get('points', [])},
        )


class NamedEntityProcessor:
    """Named Entity Tool Processor."""

    tool_name = 'named_entity'
    _INTERNAL_ATTR_PREFIX = '_'

    def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
        classification_obj = v1_annotation.get('classification') or {}
        data = {'ranges': v1_data.get('ranges', []), 'content': v1_data.get('content', '')}

        attrs: list[dict[str, Any]] = []
        for key, value in classification_obj.items():
            if key != 'class':
                attrs.append({'name': key, 'value': value})

        return {
            'id': v1_annotation.get('id', ''),
            'classification': classification_obj.get('class', ''),
            'attrs': attrs,
            'data': data,
        }

    def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
        annotation_id = v2_annotation.get('id', '')
        classification_str = v2_annotation.get('classification', '')
        attrs = v2_annotation.get('attrs', [])
        data = v2_annotation.get('data', {})

        classification: dict[str, Any] = {'class': classification_str}
        for attr in attrs:
            name = attr.get('name', '')
            value = attr.get('value')
            if not name.startswith(self._INTERNAL_ATTR_PREFIX):
                classification[name] = value

        return (
            {'id': annotation_id, 'tool': self.tool_name, 'classification': classification},
            {'id': annotation_id, 'ranges': data.get('ranges', []), 'content': data.get('content', '')},
        )


class ClassificationProcessor:
    """Classification Tool Processor."""

    tool_name = 'classification'
    _INTERNAL_ATTR_PREFIX = '_'

    def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
        classification_obj = v1_annotation.get('classification') or {}

        attrs: list[dict[str, Any]] = []
        for key, value in classification_obj.items():
            if key != 'class':
                attrs.append({'name': key, 'value': value})

        return {
            'id': v1_annotation.get('id', ''),
            'classification': classification_obj.get('class', ''),
            'attrs': attrs,
            'data': {},
        }

    def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
        annotation_id = v2_annotation.get('id', '')
        classification_str = v2_annotation.get('classification', '')
        attrs = v2_annotation.get('attrs', [])

        classification: dict[str, Any] = {'class': classification_str}
        for attr in attrs:
            name = attr.get('name', '')
            value = attr.get('value')
            if not name.startswith(self._INTERNAL_ATTR_PREFIX):
                classification[name] = value

        return (
            {'id': annotation_id, 'tool': self.tool_name, 'classification': classification},
            {'id': annotation_id},
        )


class RelationProcessor:
    """Relation Tool Processor."""

    tool_name = 'relation'
    _INTERNAL_ATTR_PREFIX = '_'

    def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
        classification_obj = v1_annotation.get('classification') or {}
        data = [v1_data.get('annotationId', ''), v1_data.get('targetAnnotationId', '')]

        attrs: list[dict[str, Any]] = []
        for key, value in classification_obj.items():
            if key != 'class':
                attrs.append({'name': key, 'value': value})

        return {
            'id': v1_annotation.get('id', ''),
            'classification': classification_obj.get('class', ''),
            'attrs': attrs,
            'data': data,
        }

    def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
        annotation_id = v2_annotation.get('id', '')
        classification_str = v2_annotation.get('classification', '')
        attrs = v2_annotation.get('attrs', [])
        data = v2_annotation.get('data', [])

        classification: dict[str, Any] = {'class': classification_str}
        for attr in attrs:
            name = attr.get('name', '')
            value = attr.get('value')
            if not name.startswith(self._INTERNAL_ATTR_PREFIX):
                classification[name] = value

        return (
            {'id': annotation_id, 'tool': self.tool_name, 'classification': classification},
            {
                'id': annotation_id,
                'annotationId': data[0] if len(data) > 0 else '',
                'targetAnnotationId': data[1] if len(data) > 1 else '',
            },
        )


class PromptProcessor:
    """Prompt Tool Processor."""

    tool_name = 'prompt'
    _INTERNAL_ATTR_PREFIX = '_'
    _DATA_FIELDS = {'input', 'model', 'displayName', 'generatedBy', 'timestamp'}

    def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
        classification_obj = v1_annotation.get('classification') or {}

        data: dict[str, Any] = {}
        for key in self._DATA_FIELDS:
            if key in v1_data:
                data[key] = v1_data[key]

        attrs: list[dict[str, Any]] = []
        for key, value in classification_obj.items():
            if key != 'class':
                attrs.append({'name': key, 'value': value})

        return {
            'id': v1_annotation.get('id', ''),
            'classification': classification_obj.get('class', ''),
            'attrs': attrs,
            'data': data,
        }

    def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
        annotation_id = v2_annotation.get('id', '')
        classification_str = v2_annotation.get('classification', '')
        attrs = v2_annotation.get('attrs', [])
        data = v2_annotation.get('data', {})

        classification: dict[str, Any] = {'class': classification_str}
        for attr in attrs:
            name = attr.get('name', '')
            value = attr.get('value')
            if not name.startswith(self._INTERNAL_ATTR_PREFIX):
                classification[name] = value

        v1_data: dict[str, Any] = {'id': annotation_id, 'tool': self.tool_name}
        for key in self._DATA_FIELDS:
            if key in data:
                v1_data[key] = data[key]

        return (
            {'id': annotation_id, 'tool': self.tool_name, 'classification': classification},
            v1_data,
        )


class AnswerProcessor:
    """Answer Tool Processor."""

    tool_name = 'answer'
    _INTERNAL_ATTR_PREFIX = '_'
    _DATA_FIELDS = {'output', 'model', 'displayName', 'generatedBy', 'promptAnnotationId', 'timestamp'}

    def to_v2(self, v1_annotation: dict[str, Any], v1_data: dict[str, Any]) -> dict[str, Any]:
        classification_obj = v1_annotation.get('classification') or {}

        data: dict[str, Any] = {}
        for key in self._DATA_FIELDS:
            if key in v1_data:
                data[key] = v1_data[key]

        attrs: list[dict[str, Any]] = []
        for key, value in classification_obj.items():
            if key != 'class':
                attrs.append({'name': key, 'value': value})

        return {
            'id': v1_annotation.get('id', ''),
            'classification': classification_obj.get('class', ''),
            'attrs': attrs,
            'data': data,
        }

    def to_v1(self, v2_annotation: dict[str, Any]) -> tuple[dict[str, Any], dict[str, Any]]:
        annotation_id = v2_annotation.get('id', '')
        classification_str = v2_annotation.get('classification', '')
        attrs = v2_annotation.get('attrs', [])
        data = v2_annotation.get('data', {})

        classification: dict[str, Any] = {'class': classification_str}
        for attr in attrs:
            name = attr.get('name', '')
            value = attr.get('value')
            if not name.startswith(self._INTERNAL_ATTR_PREFIX):
                classification[name] = value

        v1_data: dict[str, Any] = {'id': annotation_id, 'tool': self.tool_name}
        for key in self._DATA_FIELDS:
            if key in data:
                v1_data[key] = data[key]

        return (
            {'id': annotation_id, 'tool': self.tool_name, 'classification': classification},
            v1_data,
        )


__all__ = [
    'ToolProcessor',
    'BoundingBoxProcessor',
    'PolygonProcessor',
    'PolylineProcessor',
    'KeypointProcessor',
    'BoundingBox3DProcessor',
    'SegmentationProcessor',
    'Segmentation3DProcessor',
    'NamedEntityProcessor',
    'ClassificationProcessor',
    'RelationProcessor',
    'PromptProcessor',
    'AnswerProcessor',
]
