#!/usr/bin/env python3
"""
Sellematics-based Product Data Fetcher

Fetches product data via Sellematics API (requires tunnel to be running):
  systemctl status sellematics-tunnel

Provides richer data than Playwright scraping:
- Full product description
- Reviews with text
- Product characteristics
- Merchant info
- Rating and review counts
"""

import json
import re
from typing import Dict, List, Optional
import httpx

SELLEMATICS_BASE = "http://127.0.0.1:43761/api/v1"


def extract_product_id(url: str) -> Optional[str]:
    """Extract product ID from WB URL"""
    match = re.search(r'/catalog/(\d+)/', url)
    return match.group(1) if match else None


def extract_ozon_product_id(url: str) -> Optional[str]:
    """Extract product ID from Ozon URL (last numeric segment after -)"""
    url_path = url.split('?')[0].rstrip('/')
    match = re.search(r'-(\d+)$', url_path)
    if not match:
        match = re.search(r'/product/(\d+)$', url_path)
    return match.group(1) if match else None


def fetch_product_info(product_id: str, mall: str = "wb") -> Optional[Dict]:
    """
    Fetch basic product info from Sellematics

    Returns: dict with name, brand, rating, images, reviews_count, etc.
    """
    try:
        r = httpx.post(
            f"{SELLEMATICS_BASE}/products",
            data={"mall": mall, "id": product_id},
            timeout=30
        )
        data = r.json()

        if not data.get("products"):
            return None

        product = data["products"][0].get("current", {})

        default_url = (f"https://www.ozon.ru/product/{product_id}/" if mall == "ozon"
                       else f"https://www.wildberries.ru/catalog/{product_id}/detail.aspx")
        return {
            "product_id": str(product.get("id", product_id)),
            "name": product.get("name", ""),
            "brand": product.get("brand", ""),
            "url": product.get("url", default_url),
            "rating": product.get("rate"),
            "rating_count": product.get("ratingCount", 0),
            "reviews_count": product.get("reviews", 0),
            "images": product.get("images", []),
            "subjects": product.get("subjects", []),
            "merchant": product.get("merchants", [{}])[0] if product.get("merchants") else {},
            "characteristics": product.get("characteristics", []),
        }

    except Exception as e:
        print(f"Error fetching product info: {e}")
        return None


def fetch_product_description(product_id: str, mall: str = "wb") -> Optional[str]:
    """
    Fetch full product description from archive

    Returns: description text string
    """
    try:
        # First get available description versions
        r = httpx.post(
            f"{SELLEMATICS_BASE}/archive/product/description/version",
            data={"mall": mall, "id": product_id},
            timeout=30
        )
        versions = r.json().get("payload", [])

        if not versions:
            return None

        # Get latest version
        latest_ts = max(versions)

        r = httpx.post(
            f"{SELLEMATICS_BASE}/archive/product/description/text",
            data={"mall": mall, "id": product_id, "ts": str(latest_ts)},
            timeout=30
        )
        texts = r.json().get("payload", {})

        return texts.get(str(latest_ts))

    except Exception as e:
        print(f"Error fetching description: {e}")
        return None


def fetch_reviews(product_id: str, mall: str = "wb", limit: int = 300, max_texts: int = 50) -> List[Dict]:
    """
    Fetch product reviews with text for story inspiration.

    Fetches many reviews (most have no text), extracts text for those that have it,
    returns only reviews WITH actual text content.

    Args:
        product_id: Product ID
        mall: Marketplace identifier ('wb' or 'ozon')
        limit: How many reviews to fetch from API (many have no text)
        max_texts: Stop after collecting this many texts

    Returns: list of {id, rating, text, timestamp} - ONLY reviews with text
    """
    reviews_with_text = []

    try:
        # Get review list - fetch many to find ones with text
        r = httpx.post(
            f"{SELLEMATICS_BASE}/review/list",
            data={
                "mall": mall,
                "id": product_id,
                "limit": limit,
                "sort": "review_rate_desc"  # Best rated first (positive reviews)
            },
            timeout=30
        )
        review_list = r.json().get("payload", [])

        if not review_list:
            return []

        # Filter for 5-star reviews only (best for story inspiration)
        positive_reviews = [r for r in review_list if r.get("rate", 0) == 5]

        # Fetch texts until we have enough or run out of reviews
        for rev in positive_reviews:
            if len(reviews_with_text) >= max_texts:
                break

            rid = rev["id"]
            try:
                r = httpx.post(
                    f"{SELLEMATICS_BASE}/review/comment",
                    data={"mall": mall, "id": rid},
                    timeout=10
                )
                payload = r.json().get("payload", [])
                if payload and payload[0].get("comment"):
                    text = payload[0]["comment"]
                    # Clean up: strip "Достоинства: " prefix if it's the only content
                    clean_text = text
                    if clean_text.startswith("Достоинства: "):
                        clean_text = clean_text[13:]
                    if clean_text.startswith("Недостатки: "):
                        clean_text = clean_text[12:]

                    # Only include if there's actual content (not just "Красивая")
                    if len(clean_text.strip()) > 15:
                        reviews_with_text.append({
                            "id": rid,
                            "rating": rev.get("rate", 5),
                            "text": text,  # Keep original with prefix
                            "timestamp": rev.get("ts"),
                        })
            except:
                continue

    except Exception as e:
        print(f"Error fetching reviews: {e}")

    return reviews_with_text


def fetch_wb_product_sellematics(url: str) -> Optional[Dict]:
    """
    Main function: fetch complete product data via Sellematics API

    Args:
        url: Wildberries product URL

    Returns:
        Product data dict compatible with pipeline expectations:
        {
            name, brand, description, price, images, reviews,
            specifications, product_id, url, rating, reviews_count
        }
    """
    product_id = extract_product_id(url)
    if not product_id:
        print(f"Could not extract product ID from URL: {url}")
        return None

    print(f"Fetching product {product_id} via Sellematics API...")

    # Fetch basic info
    info = fetch_product_info(product_id)
    if not info:
        print("Failed to fetch product info from Sellematics")
        return None

    print(f"  Name: {info['name']}")
    print(f"  Brand: {info['brand']}")
    print(f"  Rating: {info['rating']} ({info['rating_count']} ratings)")
    print(f"  Reviews: {info['reviews_count']}")

    # Fetch description
    description = fetch_product_description(product_id, mall="wb")
    if description:
        print(f"  Description: {len(description)} chars")
    else:
        print("  Description: not available")
        description = ""

    # Fetch reviews for story inspiration (only returns reviews WITH text)
    reviews_raw = fetch_reviews(product_id, mall="wb", limit=300, max_texts=50)
    reviews = []
    for r in reviews_raw:
        reviews.append({
            "rating": r["rating"],
            "text": r["text"][:500],  # Limit text length
            "author": "Anonymous",
            "date": "",
        })
    print(f"  Reviews with text: {len(reviews)}")

    # Format specifications from characteristics
    specifications = []
    for char in info.get("characteristics", []):
        specifications.append({
            "name": char.get("name", ""),
            "value": char.get("value", "")
        })

    # Format category from subjects
    categories = [s.get("name", "") for s in info.get("subjects", [])]

    return {
        "product_id": product_id,
        "url": info["url"],
        "name": info["name"],
        "brand": info["brand"],
        "description": description[:2000] if description else "",
        "price": "",  # Price not available in this API
        "images": info["images"],
        "reviews": reviews[:50],  # Include many for story inspiration
        "specifications": specifications[:30],
        "rating": info["rating"],
        "rating_count": info["rating_count"],
        "reviews_count": info["reviews_count"],
        "categories": categories,
        "merchant": info.get("merchant", {}),
        "_source": "sellematics"
    }


def fetch_ozon_product_sellematics(url: str) -> Optional[Dict]:
    """
    Fetch complete Ozon product data via Sellematics API

    Args:
        url: Ozon product URL

    Returns:
        Product data dict compatible with pipeline expectations
    """
    product_id = extract_ozon_product_id(url)
    if not product_id:
        print(f"Could not extract Ozon product ID from URL: {url}")
        return None

    print(f"Fetching Ozon product {product_id} via Sellematics API...")

    # Fetch basic info
    info = fetch_product_info(product_id, mall="ozon")
    if not info:
        print("Failed to fetch Ozon product info from Sellematics")
        return None

    print(f"  Name: {info['name']}")
    print(f"  Brand: {info['brand']}")
    print(f"  Rating: {info['rating']} ({info['rating_count']} ratings)")
    print(f"  Reviews: {info['reviews_count']}")

    # Fetch description
    description = fetch_product_description(product_id, mall="ozon")
    if description:
        print(f"  Description: {len(description)} chars")
    else:
        print("  Description: not available")
        description = ""

    # Fetch reviews for story inspiration
    reviews_raw = fetch_reviews(product_id, mall="ozon", limit=300, max_texts=50)
    reviews = []
    for r in reviews_raw:
        reviews.append({
            "rating": r["rating"],
            "text": r["text"][:500],
            "author": "Anonymous",
            "date": "",
        })
    print(f"  Reviews with text: {len(reviews)}")

    # Format specifications from characteristics
    specifications = []
    for char in info.get("characteristics", []):
        specifications.append({
            "name": char.get("name", ""),
            "value": char.get("value", "")
        })

    # Format category from subjects
    categories = [s.get("name", "") for s in info.get("subjects", [])]

    return {
        "product_id": product_id,
        "url": info["url"],
        "name": info["name"],
        "brand": info["brand"],
        "description": description[:2000] if description else "",
        "price": "",
        "images": info["images"],
        "reviews": reviews[:50],
        "specifications": specifications[:30],
        "rating": info["rating"],
        "rating_count": info["rating_count"],
        "reviews_count": info["reviews_count"],
        "categories": categories,
        "merchant": info.get("merchant", {}),
        "_source": "sellematics"
    }


# === Public Sellematics API (fallback, no SSH tunnel needed) ===

SELLEMATICS_PUBLIC_BASE = "https://sellematics.com/public/api/v1/tg-bot-ozon"
SELLEMATICS_PUBLIC_TOKEN = "n6rccrVChF9cP20YJmFs"


def fetch_ozon_product_public(url: str) -> Optional[Dict]:
    """
    Fetch Ozon product data via public Sellematics API (fallback).
    No SSH tunnel needed. Has product info + images, but no reviews.
    """
    product_id = extract_ozon_product_id(url)
    if not product_id:
        print(f"Could not extract Ozon product ID from URL: {url}")
        return None

    print(f"Fetching Ozon product {product_id} via Sellematics public API...")

    headers = {
        "Access-Token": SELLEMATICS_PUBLIC_TOKEN,
        "Accept": "application/json",
    }

    # Fetch product info
    try:
        resp = httpx.get(
            f"{SELLEMATICS_PUBLIC_BASE}/product-info",
            params={"productId": product_id},
            headers=headers,
            timeout=15,
        )
        if resp.status_code == 404:
            print(f"  Product {product_id} not found in public API")
            return None
        resp.raise_for_status()
        data = resp.json()
    except Exception as e:
        print(f"  Public API product-info failed: {e}")
        return None

    results = data.get("result", [])
    if not results:
        print("  Empty result from public API")
        return None

    item = results[0]
    name = item.get("name", "")
    brand = item.get("brand_name", "")
    description = item.get("description", "")
    seller = item.get("seller_name", "")

    if not name:
        print(f"  Product {product_id} has no name in public API (empty data)")
        return None

    print(f"  Name: {name}")
    print(f"  Brand: {brand}")

    # Collect images: cover_image + images list
    images = []
    cover = item.get("cover_image", "")
    if cover:
        images.append(cover)
    for img_url in item.get("images", []):
        if img_url not in images:
            images.append(img_url)

    # Also try dedicated images endpoint for additional images
    try:
        resp_img = httpx.get(
            f"{SELLEMATICS_PUBLIC_BASE}/product-images",
            params={"productId": product_id},
            headers=headers,
            timeout=10,
        )
        if resp_img.status_code == 200:
            img_list = resp_img.json()
            if isinstance(img_list, list):
                for img_url in img_list:
                    if img_url not in images:
                        images.append(img_url)
    except Exception:
        pass  # images from product-info are enough

    print(f"  Images: {len(images)}")

    # Format attributes as specifications
    specifications = []
    for attr in item.get("attributes", []):
        attr_name = attr.get("name", "")
        values = attr.get("values", [])
        if attr_name and values:
            specifications.append({
                "name": attr_name,
                "value": ", ".join(values)
            })

    # Format categories
    categories = [c.get("name", "") for c in item.get("category", []) if c.get("name")]

    print(f"  Specifications: {len(specifications)}")
    print(f"  Description: {len(description)} chars")

    return {
        "product_id": product_id,
        "url": f"https://www.ozon.ru/product/{product_id}/",
        "name": name,
        "brand": brand,
        "description": description[:2000] if description else "",
        "price": "",
        "images": images,
        "reviews": [],  # public API has no reviews
        "specifications": specifications[:30],
        "rating": None,
        "rating_count": 0,
        "reviews_count": 0,
        "categories": categories,
        "merchant": {"name": seller} if seller else {},
        "_source": "sellematics_public"
    }


def is_sellematics_available() -> bool:
    """Check if Sellematics API is accessible"""
    try:
        r = httpx.get(f"{SELLEMATICS_BASE}/", timeout=5)
        return r.status_code < 500
    except:
        return False


if __name__ == "__main__":
    import sys

    if len(sys.argv) < 2:
        print("Usage: python sellematics_fetcher.py <URL>")
        print("  Supports: Wildberries and Ozon URLs")
        print("\nRequires sellematics-tunnel service to be running:")
        print("  systemctl status sellematics-tunnel")
        sys.exit(1)

    url = sys.argv[1]

    if not is_sellematics_available():
        print("ERROR: Sellematics API not available")
        print("Start the tunnel: sudo systemctl start sellematics-tunnel")
        sys.exit(1)

    # Auto-detect marketplace from URL
    if 'ozon.ru' in url:
        data = fetch_ozon_product_sellematics(url)
    else:
        data = fetch_wb_product_sellematics(url)

    if data:
        print("\n" + "="*50)
        print(json.dumps(data, indent=2, ensure_ascii=False))
    else:
        print("Failed to fetch product data")
