from typing import Dict, Any, Optional
import structlog
import re

from ..menu_extraction.models.dto import MenuExtractionResponse, MenuItem, MenuCategory

logger = structlog.get_logger("insert_menu_items.adapter")


class CreateMenuAdapter:
    """Transforms MenuExtractionResponse to createMenu.php format"""

    @staticmethod
    def transform(
        menu_response: MenuExtractionResponse,
        city: str,
        token: str
    ) -> Dict[str, Any]:
        """
        Transform menu extraction response to createMenu.php request format

        Args:
            menu_response: Response from menu_extraction service
            city: City identifier
            token: API token for createMenu.php

        Returns:
            Dictionary matching createMenu.php expected format
        """
        logger.info(
            "adapter_transform_started",
            categories_count=len(menu_response.menu.categories),
            city=city
        )

        menu_items = []

        for category in menu_response.menu.categories:
            transformed_category = CreateMenuAdapter._transform_category(category)
            menu_items.append(transformed_category)

        result = {
            "city": city,
            "token": token,
            "menu": menu_items
        }

        logger.info(
            "adapter_transform_completed",
            categories_count=len(menu_items),
            city=city
        )

        return result

    @staticmethod
    def _transform_category(category: MenuCategory) -> Dict[str, Any]:
        """Transform a single category"""

        logger.debug(
            "adapter_transforming_category",
            category_name=category.name,
            items_count=len(category.items),
            has_description=bool(category.description)
        )

        return {
            "name": category.name,  # Source language name
            "name_uk": category.name,  # Target language name
            "description": category.description or "",
            "description_uk": category.description_uk or "",  # English description from extraction
            "items": [
                CreateMenuAdapter._transform_item(item)
                for item in category.items
            ]
        }

    @staticmethod
    def _transform_item(item: MenuItem) -> Dict[str, Any]:
        """Transform a single menu item"""

        # Extract price value (convert string to float)
        price = CreateMenuAdapter._extract_price(item.price)

        # Extract serving size (gramaj)
        gramaj = CreateMenuAdapter._extract_gramaj(item.serving_size)

        # Use measure from extraction response (0=gr, 1=ml, 2=pc., 3=cm)
        # Default to 0 (grams) if not provided
        measure = item.measure if item.measure is not None else 0

        logger.debug(
            "adapter_transforming_item",
            item_name=item.name,
            original_price=item.price,
            extracted_price=price,
            original_serving=item.serving_size,
            extracted_gramaj=gramaj,
            measure_type=measure
        )

        return {
            "name": item.name,  # Source language
            "name_uk": item.name,  # Target language (from translation)
            "description": item.description or "",
            "description_uk": item.description_uk or "",  # English description from extraction
            "measure": measure,  # Use measure from extraction (0=gr, 1=ml, 2=pc., 3=cm)
            "items": [
                {
                    "name": "",  # Empty - no default portion name
                    "gramaj": gramaj,
                    "cena": price
                }
            ]
        }

    @staticmethod
    def _extract_price(price_str: Optional[str]) -> float:
        """
        Extract numeric price from price string

        Examples:
            "15.00 лв" -> 15.0
            "€10" -> 10.0
            "20" -> 20.0
        """
        if not price_str:
            return 0.0

        try:
            # Remove currency symbols and extract numbers
            numbers = re.findall(r'\d+\.?\d*', price_str)
            if numbers:
                return float(numbers[0])
            return 0.0
        except Exception as e:
            logger.warning("price_extraction_failed", price_str=price_str, error=str(e))
            return 0.0

    @staticmethod
    def _extract_gramaj(serving_size: Optional[str]) -> int:
        """
        Extract gramaj (weight/volume) from serving size string.

        Handles decimal-fraction notation used by some restaurants that print the
        unit (гр/мл) as a column header and write values like 0.350 (= 350 ml)
        or 0.5 (= 500 g). Any numeric value less than 10 is treated as a kg/L
        fraction and multiplied by 1000.

        Examples:
            "100g"     -> 100
            "250 гр"   -> 250
            "1kg"      -> 1000
            "0.350"    -> 350  (restaurant column-header notation)
            "0.5 г"    -> 500
            "0.350 мл" -> 350
            "1.5 кг"   -> 1500
        """
        if not serving_size:
            return 100  # Default 100g

        try:
            # Match decimal or integer followed by a unit
            match = re.search(
                r'(\d+(?:\.\d+)?)\s*(g|gr|kg|г|гр|кг|ml|мл|l|л)',
                serving_size.lower()
            )
            if match:
                value = float(match.group(1))
                unit = match.group(2)

                # Explicit large-unit conversion
                if unit in ('kg', 'кг', 'l', 'л'):
                    return round(value * 1000)

                # Small decimal with small unit → column-header notation (0.350 г = 350 g)
                if value < 10:
                    return round(value * 1000)

                return round(value)

            # No unit found — prefer decimal match so "0.350" parses as 0.35, not 0.
            # Only apply × 1000 for decimal fractions (0.350, 0.5) — not bare integers
            # like "2" which are likely piece counts and should stay as-is.
            number_match = re.search(r'\d+\.\d+|\d+', serving_size)
            if number_match:
                raw = number_match.group()
                value = float(raw)
                is_decimal = '.' in raw
                if is_decimal and value < 10:
                    return round(value * 1000)
                return round(value)

            return 100  # Default
        except Exception as e:
            logger.warning("gramaj_extraction_failed", serving_size=serving_size, error=str(e))
            return 100
