"""Clipboard parser and serializer for PI planet setups."""

import json
import re
from typing import Any

# Known PI structure type IDs (extend from SDE as needed)
EXTRACTOR_TYPE_IDS = {2848, 3060, 3061, 3062, 3063, 3064, 3067, 3068}
FACTORY_TYPE_IDS = {2469, 2470, 2471, 2472, 2473, 2474, 2480, 2481, 2482, 2483, 2484, 2485}
STORAGE_TYPE_IDS = {2256, 2542, 2543, 2544}
LAUNCHPAD_TYPE_IDS = {2542, 2543, 2544}
COMMAND_CENTER_TYPE_IDS = {2524, 2525, 2534, 2535, 2536, 2549, 2550, 2551, 2552, 2553, 2554, 2555, 2556, 2557}


class ClipboardParser:
    """Parse EVE PI clipboard text into structured template data."""

    def parse(self, clipboard_text: str) -> dict[str, Any]:
        """Parse clipboard text and return structured data.

        Args:
            clipboard_text: Raw text pasted from EVE PI clipboard.

        Returns:
            dict with keys: data, pins, pin_count, factory_count,
            extractor_count, output_products

        Raises:
            ValueError: If clipboard text cannot be parsed.
        """
        if not clipboard_text or not clipboard_text.strip():
            raise ValueError("Clipboard text is empty")

        lines = clipboard_text.strip().split("\n")
        pins = []
        links = []
        routes = []

        current_section = None
        pin_index = 0

        for line in lines:
            line = line.strip()
            if not line:
                continue

            # Detect section headers
            if line.lower().startswith("pins:") or line.lower() == "pins":
                current_section = "pins"
                continue
            elif line.lower().startswith("links:") or line.lower() == "links":
                current_section = "links"
                continue
            elif line.lower().startswith("routes:") or line.lower() == "routes":
                current_section = "routes"
                continue

            if current_section == "pins":
                pin = self._parse_pin_line(line, pin_index)
                if pin:
                    pins.append(pin)
                    pin_index += 1
            elif current_section == "links":
                link = self._parse_link_line(line)
                if link:
                    links.append(link)
            elif current_section == "routes":
                route = self._parse_route_line(line)
                if route:
                    routes.append(route)

        if not pins:
            raise ValueError("No pins found in clipboard text")

        # Classify pins
        factory_count = 0
        extractor_count = 0
        output_products = []

        for pin in pins:
            type_id = pin.get("type_id", 0)
            if type_id in EXTRACTOR_TYPE_IDS:
                pin["is_extractor"] = True
                extractor_count += 1
            elif type_id in FACTORY_TYPE_IDS:
                pin["is_factory"] = True
                factory_count += 1
                output_name = pin.get("factory_output_name", "")
                if output_name and output_name not in output_products:
                    output_products.append(output_name)
            elif type_id in STORAGE_TYPE_IDS or type_id in LAUNCHPAD_TYPE_IDS:
                pin["is_storage"] = True

        return {
            "data": {
                "pins": [self._pin_to_data(p) for p in pins],
                "links": links,
                "routes": routes,
            },
            "pins": pins,
            "pin_count": len(pins),
            "factory_count": factory_count,
            "extractor_count": extractor_count,
            "output_products": output_products,
        }

    def _parse_pin_line(self, line: str, index: int) -> dict | None:
        """Parse a single pin line from clipboard."""
        # Expected format: type_id\ttype_name\tlatitude\tlongitude[\tschematic_id]
        parts = line.split("\t")
        if len(parts) < 2:
            # Try comma-separated
            parts = line.split(",")
        if len(parts) < 2:
            return None

        try:
            type_id = int(parts[0].strip())
        except (ValueError, IndexError):
            return None

        type_name = parts[1].strip() if len(parts) > 1 else ""
        latitude = float(parts[2].strip()) if len(parts) > 2 else 0.0
        longitude = float(parts[3].strip()) if len(parts) > 3 else 0.0
        schematic_id = int(parts[4].strip()) if len(parts) > 4 and parts[4].strip() else None

        return {
            "pin_index": index,
            "type_id": type_id,
            "type_name": type_name,
            "latitude": latitude,
            "longitude": longitude,
            "schematic_id": schematic_id,
            "is_extractor": False,
            "is_factory": False,
            "is_storage": False,
            "factory_tier": 0,
            "factory_output_type_id": None,
            "factory_output_name": "",
            "group_id": 0,
        }

    def _parse_link_line(self, line: str) -> dict | None:
        """Parse a link line: source_pin_index\tdest_pin_index."""
        parts = line.split("\t")
        if len(parts) < 2:
            parts = line.split(",")
        if len(parts) < 2:
            return None
        try:
            return {
                "source": int(parts[0].strip()),
                "destination": int(parts[1].strip()),
            }
        except ValueError:
            return None

    def _parse_route_line(self, line: str) -> dict | None:
        """Parse a route line: source_pin\tdest_pin\ttype_id\tquantity."""
        parts = line.split("\t")
        if len(parts) < 2:
            parts = line.split(",")
        if len(parts) < 3:
            return None
        try:
            return {
                "source": int(parts[0].strip()),
                "destination": int(parts[1].strip()),
                "type_id": int(parts[2].strip()),
                "quantity": int(parts[3].strip()) if len(parts) > 3 else 0,
            }
        except ValueError:
            return None

    def _pin_to_data(self, pin: dict) -> dict:
        """Convert pin dict to JSON-safe data dict."""
        return {
            "type_id": pin["type_id"],
            "type_name": pin["type_name"],
            "latitude": pin["latitude"],
            "longitude": pin["longitude"],
            "schematic_id": pin.get("schematic_id"),
            "is_extractor": pin.get("is_extractor", False),
            "is_factory": pin.get("is_factory", False),
            "is_storage": pin.get("is_storage", False),
        }


class ClipboardSerializer:
    """Serialize a PlanetTemplate back to clipboard text."""

    def serialize(self, template) -> str:
        """Convert a template to clipboard-pasteable text.

        Args:
            template: PlanetTemplate model instance.

        Returns:
            String formatted for EVE PI clipboard paste.
        """
        if template.clipboard_raw:
            return template.clipboard_raw

        lines = ["Pins:"]
        data = template.template_data or {}
        pins = data.get("pins", [])

        for pin in pins:
            parts = [
                str(pin.get("type_id", 0)),
                pin.get("type_name", ""),
                f"{pin.get('latitude', 0.0):.6f}",
                f"{pin.get('longitude', 0.0):.6f}",
            ]
            schematic = pin.get("schematic_id")
            if schematic:
                parts.append(str(schematic))
            lines.append("\t".join(parts))

        links = data.get("links", [])
        if links:
            lines.append("")
            lines.append("Links:")
            for link in links:
                lines.append(f"{link['source']}\t{link['destination']}")

        routes = data.get("routes", [])
        if routes:
            lines.append("")
            lines.append("Routes:")
            for route in routes:
                parts = [
                    str(route["source"]),
                    str(route["destination"]),
                    str(route.get("type_id", 0)),
                ]
                if route.get("quantity"):
                    parts.append(str(route["quantity"]))
                lines.append("\t".join(parts))

        return "\n".join(lines)
