"""
EchoTik API Client - TikTok Shop Analytics (Kalodata-style)

Base URL: https://open.echotik.live/api/v2
Auth: HTTP Basic Auth (username:password)
Docs: https://opendoc.echotik.live/openapi

Endpoints used:
- Product: list, detail, trend, ranklist, video/list, influencer/list
- Seller: list, detail, trend, ranklist, product/list
- Influencer: ranklist, detail
- Video: ranklist
"""

import json
import logging
from datetime import datetime, timedelta

import requests
from requests.auth import HTTPBasicAuth

logger = logging.getLogger(__name__)

BASE_URL = "https://open.echotik.live/api/v2"
TIMEOUT = 30
DEFAULT_REGION = "BR"


class EchoTikClient:
    def __init__(self, username: str, api_key: str):
        self.auth = HTTPBasicAuth(username, api_key)

    def _get(self, path: str, params: dict = None) -> dict:
        """Make GET request to EchoTik API."""
        url = f"{BASE_URL}{path}"
        try:
            r = requests.get(url, auth=self.auth, params=params, timeout=TIMEOUT)
            r.raise_for_status()
            data = r.json()
            if data.get("code") != 0:
                msg = data.get("message", "Unknown error")
                logger.warning(f"EchoTik API error ({path}): {msg}")
                raise ValueError(f"EchoTik: {msg}")
            return data
        except requests.exceptions.HTTPError as e:
            status = e.response.status_code if e.response else 0
            if status == 401:
                raise ValueError("EchoTik: Credenciais invalidas")
            if status == 403:
                raise ValueError("EchoTik: Sem permissao ou limite de requests atingido")
            raise
        except ValueError:
            raise
        except Exception as e:
            logger.error(f"EchoTik API error ({path}): {e}")
            raise

    # ─── Product Endpoints ───────────────────────────────────

    def product_ranking(self, date: str = "", rank_field: int = 1,
                        rank_type: int = 1, category_id: str = "",
                        page: int = 1, page_size: int = 10) -> list:
        """Get product rankings.

        rank_field: 1=sales_count, 2=sales_gmv, 3=sales_volume
        rank_type: 1=daily
        """
        dates_to_try = [date] if date else [
            (datetime.utcnow() - timedelta(days=d)).strftime("%Y-%m-%d")
            for d in range(1, 5)
        ]
        for try_date in dates_to_try:
            params = {
                "region": DEFAULT_REGION,
                "date": try_date,
                "product_rank_field": rank_field,
                "rank_type": rank_type,
                "page_num": page,
                "page_size": min(page_size, 10),
            }
            if category_id:
                params["category_id"] = category_id
            try:
                data = self._get("/product/ranklist", params)
                items = data.get("data") or []
                if items:
                    return [_normalize_product(p) for p in items]
            except Exception:
                continue
        return []

    def product_search(self, keyword: str = "", page: int = 1,
                       page_size: int = 10) -> list:
        """Search products by keyword."""
        params = {
            "region": DEFAULT_REGION,
            "page_num": page,
            "page_size": min(page_size, 10),
        }
        if keyword:
            params["keyword"] = keyword
        data = self._get("/product/list", params)
        return [_normalize_product(p) for p in (data.get("data") or [])]

    def product_detail(self, product_id: str) -> dict:
        """Get detailed product info."""
        data = self._get("/product/detail", {"product_ids": product_id})
        items = data.get("data") or []
        if not items:
            raise ValueError("Produto nao encontrado")
        return _normalize_product_detail(items[0])

    def product_trend(self, product_id: str, days: int = 7) -> list:
        """Get product sales trend over time."""
        end = (datetime.utcnow() - timedelta(days=1)).strftime("%Y-%m-%d")
        start = (datetime.utcnow() - timedelta(days=days)).strftime("%Y-%m-%d")
        data = self._get("/product/trend", {
            "product_id": product_id,
            "region": DEFAULT_REGION,
            "start_date": start,
            "end_date": end,
            "page_num": 1,
            "page_size": 10,
        })
        return [_normalize_trend(t) for t in (data.get("data") or [])]

    def product_videos(self, product_id: str, page: int = 1,
                       page_size: int = 10) -> list:
        """Get videos promoting a product."""
        data = self._get("/product/video/list", {
            "product_id": product_id,
            "region": DEFAULT_REGION,
            "page_num": page,
            "page_size": min(page_size, 10),
        })
        return [_normalize_video(v) for v in (data.get("data") or [])]

    def product_influencers(self, product_id: str, page: int = 1,
                            page_size: int = 10) -> list:
        """Get influencers selling a product."""
        data = self._get("/product/influencer/list", {
            "product_id": product_id,
            "region": DEFAULT_REGION,
            "page_num": page,
            "page_size": min(page_size, 10),
        })
        return [_normalize_influencer(i) for i in (data.get("data") or [])]

    # ─── Seller Endpoints ────────────────────────────────────

    def seller_ranking(self, date: str = "", rank_field: int = 1,
                       rank_type: int = 1, page: int = 1,
                       page_size: int = 10) -> list:
        """Get shop/seller rankings.

        rank_field: 1=sales_count, 2=sales_gmv
        """
        dates_to_try = [date] if date else [
            (datetime.utcnow() - timedelta(days=d)).strftime("%Y-%m-%d")
            for d in range(1, 5)
        ]
        for try_date in dates_to_try:
            params = {
                "region": DEFAULT_REGION,
                "date": try_date,
                "seller_rank_field": rank_field,
                "rank_type": rank_type,
                "page_num": page,
                "page_size": min(page_size, 10),
            }
            try:
                data = self._get("/seller/ranklist", params)
                items = data.get("data") or []
                if items:
                    return [_normalize_seller(s) for s in items]
            except Exception:
                continue
        return []

    def seller_detail(self, seller_id: str) -> dict:
        """Get detailed shop/seller info."""
        data = self._get("/seller/detail", {"seller_id": seller_id})
        items = data.get("data") or []
        if not items:
            raise ValueError("Loja nao encontrada")
        return _normalize_seller_detail(items[0])

    def seller_products(self, seller_id: str, page: int = 1,
                        page_size: int = 10) -> list:
        """Get products from a shop."""
        data = self._get("/seller/product/list", {
            "seller_id": seller_id,
            "page_num": page,
            "page_size": min(page_size, 10),
        })
        return [_normalize_product(p) for p in (data.get("data") or [])]

    def seller_search(self, keyword: str = "", page: int = 1,
                      page_size: int = 10) -> list:
        """Search sellers/shops."""
        params = {
            "region": DEFAULT_REGION,
            "page_num": page,
            "page_size": min(page_size, 10),
        }
        if keyword:
            params["keyword"] = keyword
        data = self._get("/seller/list", params)
        return [_normalize_seller(s) for s in (data.get("data") or [])]

    # ─── Influencer Endpoints ────────────────────────────────

    def influencer_ranking(self, date: str = "", rank_field: int = 1,
                           rank_type: int = 1, page: int = 1,
                           page_size: int = 10) -> list:
        """Get influencer/creator rankings.

        rank_field: 1=followers, 2=sales_count, 3=sales_gmv
        """
        dates_to_try = [date] if date else [
            (datetime.utcnow() - timedelta(days=d)).strftime("%Y-%m-%d")
            for d in range(2, 6)
        ]
        for try_date in dates_to_try:
            params = {
                "region": DEFAULT_REGION,
                "date": try_date,
                "influencer_rank_field": rank_field,
                "rank_type": rank_type,
                "page_num": page,
                "page_size": min(page_size, 10),
            }
            try:
                data = self._get("/influencer/ranklist", params)
                items = data.get("data") or []
                if items:
                    return [_normalize_influencer(i) for i in items]
            except Exception:
                continue
        return []

    # ─── Video Endpoints ─────────────────────────────────────

    def video_ranking(self, date: str = "", rank_field: int = 1,
                      rank_type: int = 1, page: int = 1,
                      page_size: int = 10) -> list:
        """Get video rankings.

        rank_field: 1=views, 2=sales
        """
        dates_to_try = [date] if date else [
            (datetime.utcnow() - timedelta(days=d)).strftime("%Y-%m-%d")
            for d in range(2, 6)
        ]
        for try_date in dates_to_try:
            params = {
                "region": DEFAULT_REGION,
                "date": try_date,
                "video_rank_field": rank_field,
                "rank_type": rank_type,
                "page_num": page,
                "page_size": min(page_size, 10),
            }
            try:
                data = self._get("/video/ranklist", params)
                items = data.get("data") or []
                if items:
                    return [_normalize_video(v) for v in items]
            except Exception:
                continue
        return []


# ─── Normalizers ─────────────────────────────────────────────

def _get_cover_url(cover_raw) -> str:
    """Extract first image URL from cover_url JSON string."""
    if not cover_raw:
        return ""
    if isinstance(cover_raw, str):
        if cover_raw.startswith("http"):
            return cover_raw
        try:
            imgs = json.loads(cover_raw)
            if isinstance(imgs, list):
                # Sort by index, pick index 0
                imgs.sort(key=lambda x: x.get("index", 99))
                return imgs[0].get("url", "") if imgs else ""
        except (json.JSONDecodeError, TypeError):
            return cover_raw
    return ""


def _cache_image_url(img_type: str, img_id: str, url: str):
    """Save image URL to DB for the proxy endpoint to fetch later."""
    if not url or not img_id:
        return
    try:
        from backend.database.connection import SessionLocal
        from backend.models.settings import AppSettings
        db = SessionLocal()
        try:
            cache_key = f"echotik_img_{img_type}_{img_id}"
            row = db.query(AppSettings).filter(AppSettings.key == cache_key).first()
            if row:
                if row.value != url:
                    row.value = url
                    db.commit()
            else:
                db.add(AppSettings(key=cache_key, value=url, category="echotik_cache"))
                db.commit()
        finally:
            db.close()
    except Exception:
        pass  # Non-critical, just cache


def _extract_sku_image(p: dict) -> str:
    """Extract first accessible image from product sale_props (TikTok CDN)."""
    props = p.get("sale_props", "")
    if isinstance(props, str):
        try:
            props = json.loads(props)
        except (json.JSONDecodeError, TypeError):
            return ""
    for prop in (props or []):
        if prop.get("has_image") == "true":
            for val in prop.get("sale_prop_values", []):
                img = val.get("image", "")
                if img and "ibyteimg.com" in img:
                    return img
    return ""


def _format_brl(value) -> str:
    """Format number as BRL currency."""
    try:
        v = float(value or 0)
        if v >= 1_000_000:
            return f"R$ {v/1_000_000:.1f}M"
        if v >= 1_000:
            return f"R$ {v/1_000:.1f}K"
        return f"R$ {v:.2f}"
    except (ValueError, TypeError):
        return "R$ 0"


def _format_number(n) -> str:
    try:
        n = int(n or 0)
        if n >= 1_000_000:
            return f"{n/1_000_000:.1f}M"
        if n >= 1_000:
            return f"{n/1_000:.1f}K"
        return str(n)
    except (ValueError, TypeError):
        return "0"


def _parse_categories(raw) -> list:
    """Parse category list from JSON string."""
    if not raw:
        return []
    if isinstance(raw, str):
        try:
            return json.loads(raw)
        except (json.JSONDecodeError, TypeError):
            return []
    if isinstance(raw, list):
        return raw
    return []


def _normalize_product(p: dict) -> dict:
    """Normalize product from list/ranklist."""
    sale_cnt = int(p.get("total_sale_cnt", 0) or 0)
    gmv = float(p.get("total_sale_gmv_amt", 0) or 0)
    price = float(p.get("spu_avg_price", 0) or p.get("min_price", 0) or 0)
    commission = float(p.get("product_commission_rate", 0) or 0)

    pid = p.get("product_id", "")
    # Prefer SKU image (TikTok CDN, accessible) over cover_url (EchoTik CDN, blocked)
    sku_img = _extract_sku_image(p)
    cover_url = sku_img or _get_cover_url(p.get("cover_url"))
    if cover_url and pid:
        _cache_image_url("products", pid, cover_url)

    return {
        "product_id": pid,
        "title": p.get("product_name", ""),
        "image": f"/api/echotik/image/products/{pid}.jpg" if pid else "",
        "price": price,
        "price_formatted": _format_brl(price),
        "min_price": float(p.get("min_price", 0) or 0),
        "max_price": float(p.get("max_price", 0) or 0),
        "discount": p.get("discount", ""),
        "sales_total": sale_cnt,
        "sales_formatted": _format_number(sale_cnt),
        "gmv_total": gmv,
        "gmv_formatted": _format_brl(gmv),
        "commission_rate": commission,
        "commission_pct": f"{commission*100:.0f}%" if commission else "",
        "rating": float(p.get("product_rating", 0) or 0),
        "review_count": int(p.get("review_count", 0) or 0),
        "influencer_count": int(p.get("total_ifl_cnt", 0) or 0),
        "video_count": int(p.get("total_video_cnt", 0) or 0),
        "live_count": int(p.get("total_live_cnt", 0) or 0),
        "seller_id": p.get("seller_id", ""),
        "category_id": p.get("category_id", ""),
        "region": p.get("region", DEFAULT_REGION),
    }


def _normalize_product_detail(p: dict) -> dict:
    """Normalize product detail (richer data)."""
    base = _normalize_product(p)

    # Add period sales data
    base.update({
        "sales_1d": int(p.get("total_sale_1d_cnt", 0) or 0),
        "sales_7d": int(p.get("total_sale_7d_cnt", 0) or 0),
        "sales_15d": int(p.get("total_sale_15d_cnt", 0) or 0),
        "sales_30d": int(p.get("total_sale_30d_cnt", 0) or 0),
        "sales_90d": int(p.get("total_sale_90d_cnt", 0) or 0),
        "gmv_1d": float(p.get("total_sale_gmv_1d_amt", 0) or 0),
        "gmv_7d": float(p.get("total_sale_gmv_7d_amt", 0) or 0),
        "gmv_15d": float(p.get("total_sale_gmv_15d_amt", 0) or 0),
        "gmv_30d": float(p.get("total_sale_gmv_30d_amt", 0) or 0),
        "gmv_90d": float(p.get("total_sale_gmv_90d_amt", 0) or 0),
        "gmv_1d_formatted": _format_brl(p.get("total_sale_gmv_1d_amt", 0)),
        "gmv_7d_formatted": _format_brl(p.get("total_sale_gmv_7d_amt", 0)),
        "gmv_30d_formatted": _format_brl(p.get("total_sale_gmv_30d_amt", 0)),
        "videos_1d": int(p.get("total_video_1d_cnt", 0) or 0),
        "videos_7d": int(p.get("total_video_7d_cnt", 0) or 0),
        "videos_30d": int(p.get("total_video_30d_cnt", 0) or 0),
        "influencers_7d": int(p.get("total_ifl_video_7d_cnt", 0) or 0),
        "influencers_30d": int(p.get("total_ifl_video_30d_cnt", 0) or 0),
        "views_total": int(p.get("total_views_cnt", 0) or p.get("total_views_15d_cnt", 0) or 0),
        "free_shipping": bool(p.get("free_shipping", 0)),
        "categories": _parse_categories(p.get("product_category_list") or p.get("most_product_category_list")),
    })

    # Parse SKUs for stock
    skus_raw = p.get("skus", "[]")
    if isinstance(skus_raw, str):
        try:
            skus_raw = json.loads(skus_raw)
        except (json.JSONDecodeError, TypeError):
            skus_raw = []
    total_stock = 0
    skus = []
    for sku in (skus_raw or []):
        stock = int(sku.get("stock", 0) or 0)
        total_stock += stock
        price_info = sku.get("real_price", {})
        skus.append({
            "sku_id": sku.get("sku_id", ""),
            "stock": stock,
            "price": price_info.get("price_str", price_info.get("sale_price", "")),
        })
    base["stock"] = total_stock
    base["skus"] = skus

    return base


def _normalize_trend(t: dict) -> dict:
    """Normalize product trend data point."""
    return {
        "date": t.get("dt", ""),
        "price": float(t.get("spu_avg_price", 0) or 0),
        "sales_daily": int(t.get("total_sale_1d_cnt", 0) or 0),
        "sales_total": int(t.get("total_sale_cnt", 0) or 0),
        "gmv_daily": float(t.get("total_sale_gmv_1d_amt", 0) or 0),
        "gmv_total": float(t.get("total_sale_gmv_amt", 0) or 0),
        "influencer_count": int(t.get("total_ifl_cnt", 0) or 0),
        "video_count": int(t.get("total_video_cnt", 0) or 0),
    }


def _normalize_seller(s: dict) -> dict:
    """Normalize seller from list/ranklist."""
    sale_cnt = int(s.get("total_sale_cnt", 0) or 0)
    gmv = float(s.get("total_sale_gmv_amt", 0) or 0)
    categories = _parse_categories(s.get("most_product_category_list"))
    category_name = categories[0].get("category_name", "") if categories else ""

    sid = s.get("seller_id", "")
    cover_url = _get_cover_url(s.get("cover_url"))
    if cover_url and sid:
        _cache_image_url("sellers", sid, cover_url)

    return {
        "seller_id": sid,
        "name": s.get("seller_name", ""),
        "logo": f"/api/echotik/image/sellers/{sid}.jpg" if sid else "",
        "rating": float(s.get("rating", 0) or 0),
        "category": category_name,
        "product_count": int(s.get("total_product_cnt", 0) or 0),
        "sales_total": sale_cnt,
        "sales_formatted": _format_number(sale_cnt),
        "gmv_total": gmv,
        "gmv_formatted": _format_brl(gmv),
        "influencer_count": int(s.get("total_ifl_cnt", 0) or 0),
        "video_count": int(s.get("total_video_cnt", 0) or 0),
        "region": s.get("region", DEFAULT_REGION),
        "user_id": s.get("user_id", ""),
    }


def _normalize_seller_detail(s: dict) -> dict:
    """Normalize seller detail (richer data)."""
    base = _normalize_seller(s)
    base.update({
        "sales_1d": int(s.get("total_sale_1d_cnt", 0) or 0),
        "sales_7d": int(s.get("total_sale_7d_cnt", 0) or 0),
        "sales_30d": int(s.get("total_sale_30d_cnt", 0) or 0),
        "sales_90d": int(s.get("total_sale_90d_cnt", 0) or 0),
        "gmv_1d": float(s.get("total_sale_gmv_1d_amt", 0) or 0),
        "gmv_7d": float(s.get("total_sale_gmv_7d_amt", 0) or 0),
        "gmv_30d": float(s.get("total_sale_gmv_30d_amt", 0) or 0),
        "gmv_90d": float(s.get("total_sale_gmv_90d_amt", 0) or 0),
        "gmv_1d_formatted": _format_brl(s.get("total_sale_gmv_1d_amt", 0)),
        "gmv_7d_formatted": _format_brl(s.get("total_sale_gmv_7d_amt", 0)),
        "gmv_30d_formatted": _format_brl(s.get("total_sale_gmv_30d_amt", 0)),
        "total_products_crawled": int(s.get("total_crawl_product_cnt", 0) or 0),
        "avg_price": float(s.get("spu_avg_price", 0) or 0),
        "avg_price_formatted": _format_brl(s.get("spu_avg_price", 0)),
    })
    return base


def _normalize_influencer(i: dict) -> dict:
    """Normalize influencer data."""
    sale_cnt = int(i.get("total_sale_cnt", 0) or i.get("total_sale_history_cnt", 0) or 0)
    gmv = float(i.get("total_sale_gmv_amt", 0) or i.get("total_sale_gmv_history_amt", 0) or 0)

    uid = i.get("user_id", "")
    avatar_url = i.get("avatar", "")
    if avatar_url and uid:
        _cache_image_url("creators", uid, avatar_url)

    return {
        "user_id": uid,
        "username": i.get("unique_id", ""),
        "nickname": i.get("nick_name", ""),
        "avatar": f"/api/echotik/image/creators/{uid}.jpg" if uid else "",
        "category": i.get("category", ""),
        "followers": int(i.get("total_followers_cnt", 0) or 0),
        "followers_formatted": _format_number(i.get("total_followers_cnt", 0)),
        "likes": int(i.get("total_digg_cnt", 0) or 0),
        "sales_total": sale_cnt,
        "sales_formatted": _format_number(sale_cnt),
        "gmv_total": gmv,
        "gmv_formatted": _format_brl(gmv),
        "ec_score": float(i.get("ec_score", 0) or 0),
        "video_count": int(i.get("total_post_video_cnt", 0) or i.get("total_post_video_history_cnt", 0) or 0),
        "product_count": int(i.get("total_product_cnt", 0) or i.get("total_product_history_cnt", 0) or 0),
        "region": i.get("region", DEFAULT_REGION),
    }


def _normalize_video(v: dict) -> dict:
    """Normalize video data."""
    vid = v.get("video_id", "")
    thumb_url = v.get("reflow_cover", "")
    if thumb_url and vid:
        _cache_image_url("videos", vid, thumb_url)

    creator_uid = v.get("user_id", "")
    creator_avatar = v.get("avatar", "")
    if creator_avatar and creator_uid:
        _cache_image_url("creators", creator_uid, creator_avatar)

    return {
        "video_id": vid,
        "description": v.get("video_desc", ""),
        "thumbnail": f"/api/echotik/image/videos/{vid}.jpg" if vid else "",
        "duration": int(v.get("duration", 0) or 0),
        "views": int(v.get("total_views_cnt", 0) or v.get("total_views_history_cnt", 0) or 0),
        "views_formatted": _format_number(v.get("total_views_cnt", 0) or v.get("total_views_history_cnt", 0)),
        "likes": int(v.get("total_digg_cnt", 0) or v.get("total_digg_history_cnt", 0) or 0),
        "comments": int(v.get("total_comments_cnt", 0) or v.get("total_comments_history_cnt", 0) or 0),
        "shares": int(v.get("total_shares_cnt", 0) or v.get("total_shares_history_cnt", 0) or 0),
        "sales": int(v.get("total_video_sale_cnt", 0) or v.get("total_video_sale_history_cnt", 0) or 0),
        "sales_formatted": _format_number(v.get("total_video_sale_cnt", 0) or v.get("total_video_sale_history_cnt", 0)),
        "gmv": float(v.get("total_video_sale_gmv_amt", 0) or v.get("total_video_sale_gmv_history_amt", 0) or 0),
        "gmv_formatted": _format_brl(v.get("total_video_sale_gmv_amt", 0) or v.get("total_video_sale_gmv_history_amt", 0)),
        "creator_username": v.get("unique_id", ""),
        "creator_nickname": v.get("nick_name", ""),
        "creator_avatar": f"/api/echotik/image/creators/{creator_uid}.jpg" if creator_uid else "",
        "user_id": creator_uid,
        "product_id": v.get("product_id", ""),
        "hashtags": v.get("hash_tag", ""),
        "created_at": v.get("create_time", ""),
        "region": v.get("region", DEFAULT_REGION),
    }
