"""Analyzes the product listing itself (content quality, not commercials)."""
from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Optional

from ...utils.helpers import (
    as_list,
    clamp,
    first_present,
    ratio_score,
    round2,
    safe_get,
    to_float,
    to_int,
)


@dataclass
class ProductAnalysis:
    """Normalized product data plus listing scores."""

    title: str = ""
    description_length: int = 0
    image_count: int = 0
    category_id: Optional[str] = None
    property_count: int = 0
    package_length_cm: float = 0.0
    package_width_cm: float = 0.0
    package_height_cm: float = 0.0
    weight_kg: float = 0.0
    sales_count: int = 0
    review_count: int = 0
    average_rating: float = 0.0
    quality_score: float = 0.0
    completeness_score: float = 0.0

    @property
    def largest_dimension_cm(self) -> float:
        return max(self.package_length_cm, self.package_width_cm,
                   self.package_height_cm)

    def to_dict(self) -> dict:
        return {
            "title": self.title,
            "descriptionLength": self.description_length,
            "imageCount": self.image_count,
            "categoryId": self.category_id,
            "propertyCount": self.property_count,
            "package": {
                "lengthCm": round2(self.package_length_cm),
                "widthCm": round2(self.package_width_cm),
                "heightCm": round2(self.package_height_cm),
                "weightKg": round2(self.weight_kg),
            },
            "salesCount": self.sales_count,
            "reviewCount": self.review_count,
            "averageRating": round2(self.average_rating),
            "qualityScore": round(self.quality_score, 2),
            "completenessScore": round(self.completeness_score, 2),
        }


class ProductAnalyzer:
    """Single responsibility: judge the listing content.

    Quality: how convincing the listing is (title, images, description depth).
    Completeness: how many expected fields the supplier actually filled in.
    """

    IDEAL_TITLE_LENGTH = 60
    IDEAL_IMAGE_COUNT = 6
    IDEAL_DESCRIPTION_LENGTH = 500
    IDEAL_PROPERTY_COUNT = 5

    def analyze(self, product_json: dict) -> ProductAnalysis:
        base = safe_get(product_json, "ae_item_base_info_dto", {}) or {}
        result = ProductAnalysis(
            title=str(first_present(base, ("subject", "title", "product_title"),
                                    default="")),
            description_length=len(str(first_present(base, ("detail", "description",
                                                             "mobile_detail"),
                                                      default=""))),
            image_count=self._count_images(product_json),
            category_id=self._category(base),
            property_count=self._count_properties(product_json),
            sales_count=to_int(first_present(base, ("sales_count", "order_count",
                                                    "gmt_sales"))),
            review_count=to_int(first_present(base, ("evaluation_count",
                                                     "review_count", "total_reviews"))),
            average_rating=to_float(first_present(base, ("avg_evaluation_rating",
                                                         "average_rating", "rating"))),
        )
        self._parse_package(product_json, result)
        result.quality_score = self._quality_score(result)
        result.completeness_score = self._completeness_score(result)
        return result

    # ------------------------------------------------------------------ #
    @staticmethod
    def _category(base: dict) -> Optional[str]:
        value = first_present(base, ("category_id", "categoryId", "category"))
        return str(value) if value is not None else None

    @staticmethod
    def _count_images(product_json: dict) -> int:
        urls = safe_get(product_json, "ae_multimedia_info_dto.image_urls", "") or ""
        return len([u for u in str(urls).split(";") if u.strip()])

    @staticmethod
    def _count_properties(product_json: dict) -> int:
        properties = first_present(
            product_json,
            ("ae_item_properties.ae_item_property", "ae_item_properties",
             "properties"),
        )
        return len(as_list(properties))

    @staticmethod
    def _parse_package(product_json: dict, result: ProductAnalysis) -> None:
        package = safe_get(product_json, "package_info_dto", {}) or {}
        result.package_length_cm = to_float(first_present(package, ("package_length",
                                                                    "length")))
        result.package_width_cm = to_float(first_present(package, ("package_width",
                                                                   "width")))
        result.package_height_cm = to_float(first_present(package, ("package_height",
                                                                    "height")))
        result.weight_kg = to_float(first_present(package, ("gross_weight", "weight")))

    def _quality_score(self, p: ProductAnalysis) -> float:
        """Blend of content-richness signals, each saturating at an ideal value."""
        title_score = ratio_score(len(p.title), self.IDEAL_TITLE_LENGTH)
        image_score = ratio_score(p.image_count, self.IDEAL_IMAGE_COUNT)
        description_score = ratio_score(p.description_length,
                                        self.IDEAL_DESCRIPTION_LENGTH)
        property_score = ratio_score(p.property_count, self.IDEAL_PROPERTY_COUNT)
        return clamp(
            title_score * 0.30
            + image_score * 0.30
            + description_score * 0.20
            + property_score * 0.20
        )

    @staticmethod
    def _completeness_score(p: ProductAnalysis) -> float:
        """Share of expected listing fields that are actually present."""
        checks = [
            bool(p.title),
            p.description_length > 0,
            p.image_count > 0,
            p.category_id is not None,
            p.property_count > 0,
            p.largest_dimension_cm > 0,
            p.weight_kg > 0,
        ]
        return clamp((sum(checks) / len(checks)) * 100.0)
