"""
YOLO Exporter

Exports image annotations to YOLO format:
- One .txt file per image with lines: class_id cx cy w h (normalized 0-1)
- classes.txt listing class names
- data.yaml for Ultralytics compatibility
"""

import os
import logging
from typing import Optional, Tuple

from .base import BaseExporter, ExportContext, ExportResult
from .cv_utils import (
    build_category_mapping,
    polygon_to_bbox,
    normalize_bbox,
    extract_image_annotations,
    get_image_dimensions,
    get_image_filename,
)

logger = logging.getLogger(__name__)


class YOLOExporter(BaseExporter):
    format_name = "yolo"
    description = "YOLO format for object detection (Ultralytics compatible)"
    file_extensions = [".txt", ".yaml"]

    def can_export(self, context: ExportContext) -> Tuple[bool, str]:
        has_image_schema = any(
            s.get("annotation_type") == "image_annotation"
            for s in context.schemas
        )
        if not has_image_schema:
            return False, "No image_annotation schema found in config"

        # Check that we can get image dimensions
        missing_dims = []
        for ann in context.annotations:
            instance_id = ann.get("instance_id", "")
            item = context.items.get(instance_id, {})
            img_anns = extract_image_annotations(ann)
            if img_anns:
                w, h = get_image_dimensions(item)
                if w <= 0 or h <= 0:
                    missing_dims.append(instance_id)

        if missing_dims:
            return (
                False,
                f"YOLO requires image dimensions. Missing for: "
                f"{', '.join(missing_dims[:5])}"
                f"{'...' if len(missing_dims) > 5 else ''}"
            )
        return True, ""

    def export(self, context: ExportContext, output_path: str,
               options: Optional[dict] = None) -> ExportResult:
        options = options or {}
        warnings = []
        files_written = []

        category_map = build_category_mapping(context.annotations, context.schemas)
        labels_dir = os.path.join(output_path, "labels")
        os.makedirs(labels_dir, exist_ok=True)

        # Track which images have been written (handle multiple annotators)
        image_labels = {}  # filename_stem -> list of label lines

        for ann in context.annotations:
            instance_id = ann.get("instance_id", "")
            item = context.items.get(instance_id, {})
            img_anns = extract_image_annotations(ann)
            if not img_anns:
                continue

            img_w, img_h = get_image_dimensions(item)
            if img_w <= 0 or img_h <= 0:
                warnings.append(f"Skipping {instance_id}: no image dimensions")
                continue

            file_name = get_image_filename(item) or instance_id
            raw_stem = os.path.splitext(os.path.basename(file_name))[0]
            stem = "".join(c if c.isalnum() or c in "-_." else "_" for c in raw_stem)

            if stem not in image_labels:
                image_labels[stem] = []

            for schema_name, objects in img_anns:
                for obj in objects:
                    obj_type = obj.get("type", "")
                    label = obj.get("label", "")

                    if label not in category_map:
                        warnings.append(f"Unknown label '{label}' in {instance_id}")
                        continue

                    class_id = category_map[label]

                    if obj_type == "bbox":
                        x = obj.get("x", 0)
                        y = obj.get("y", 0)
                        w = obj.get("width", 0)
                        h = obj.get("height", 0)
                        cx, cy, nw, nh = normalize_bbox(x, y, w, h, img_w, img_h)
                        image_labels[stem].append(
                            f"{class_id} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}"
                        )

                    elif obj_type in ("polygon", "freeform"):
                        points = obj.get("points", [])
                        if not points:
                            continue
                        bx, by, bw, bh = polygon_to_bbox(points)
                        cx, cy, nw, nh = normalize_bbox(bx, by, bw, bh, img_w, img_h)
                        warnings.append(
                            f"{obj_type} in {instance_id} converted to enclosing bbox"
                        )
                        image_labels[stem].append(
                            f"{class_id} {cx:.6f} {cy:.6f} {nw:.6f} {nh:.6f}"
                        )

                    elif obj_type == "landmark":
                        warnings.append(
                            f"Landmark in {instance_id} skipped (not supported in YOLO)"
                        )

                    else:
                        warnings.append(
                            f"Unknown type '{obj_type}' in {instance_id}"
                        )

        # Write label files
        for stem, lines in image_labels.items():
            label_file = os.path.join(labels_dir, f"{stem}.txt")
            with open(label_file, "w") as f:
                f.write("\n".join(lines))
                if lines:
                    f.write("\n")
            files_written.append(label_file)

        # Write classes.txt
        sorted_labels = sorted(category_map.items(), key=lambda kv: kv[1])
        classes_file = os.path.join(output_path, "classes.txt")
        with open(classes_file, "w") as f:
            for name, _ in sorted_labels:
                f.write(f"{name}\n")
        files_written.append(classes_file)

        # Write data.yaml for Ultralytics
        data_yaml = os.path.join(output_path, "data.yaml")
        with open(data_yaml, "w") as f:
            f.write(f"path: {output_path}\n")
            f.write("train: images/train\n")
            f.write("val: images/val\n")
            f.write(f"nc: {len(sorted_labels)}\n")
            f.write(f"names: [{', '.join(repr(n) for n, _ in sorted_labels)}]\n")
        files_written.append(data_yaml)

        return ExportResult(
            success=True,
            format_name=self.format_name,
            files_written=files_written,
            warnings=warnings,
            stats={
                "num_images": len(image_labels),
                "num_annotations": sum(len(v) for v in image_labels.values()),
                "num_classes": len(sorted_labels),
            },
        )
