"""Pull products from Plytix API into Plytix source snapshots.

CLI:
    python -m app.jobs.plytix_pull
"""

from __future__ import annotations

import argparse
import hashlib
import json
import sys
from typing import Any, Dict, List, Optional

import pandas as pd


def run_plytix_pull(*, source_label: str = "plytix_api", dry_run: bool = False) -> Dict[str, Any]:
    from db.repositories import SqlAlchemyIngestRepository
    from db.session import get_session
    from db.source_imports import import_plytix_source_dataframe
    from plytix.client_factory import build_plytix_client
    from plytix.product_pull import fetch_plytix_products
    from settings import load_plytix_api_config

    cfg = load_plytix_api_config()
    try:
        client = build_plytix_client(cfg)
    except ValueError as exc:
        return {"status": "failed", "error": str(exc)}
    with get_session() as session:
        products = fetch_plytix_products(
            client,
            cfg.products_path,
            page_size=cfg.page_size,
            max_pages=cfg.max_pages,
            session=session,
        )
        if not products:
            return {"status": "failed", "error": "No products returned from Plytix API"}
        if dry_run:
            skus = {str(item.get("sku") or "").strip() for item in products if isinstance(item, dict)}
            skus.discard("")
            return {
                "status": "ok",
                "dry_run": True,
                "source": "plytix_api",
                "input_rows": len(products),
                "distinct_skus": len(skus),
            }
        df = pd.DataFrame(products)
        payload_hash = hashlib.sha256(json.dumps(products, sort_keys=True, default=str).encode("utf-8")).hexdigest()
        repo = SqlAlchemyIngestRepository(session)
        ingest_id, stats = import_plytix_source_dataframe(
            repo,
            df,
            file_name=f"{source_label}.json",
            file_hash=payload_hash,
        )
        session.commit()
    return {"status": "ok", "source": "plytix_api", "ingest_id": ingest_id, **stats}


def _fetch_product_pages(
    client: Any,
    path: str,
    *,
    page_size: int,
    max_pages: int,
    attributes: Optional[List[str]] = None,
) -> List[Dict[str, Any]]:
    """Plytix v1 list endpoints use POST .../search with pagination in the JSON body."""
    search_path = _normalize_search_path(path)
    products: List[Dict[str, Any]] = []
    page = 1
    while page <= max_pages:
        body: Dict[str, Any] = {"pagination": {"page": page, "page_size": page_size}}
        if attributes:
            body["attributes"] = list(attributes)
        payload = client.post(search_path, json=body)
        items = _items_from_payload(payload)
        products.extend([item for item in items if isinstance(item, dict)])
        if not items or not _search_has_next_page(payload, page=page, page_size=page_size, item_count=len(items)):
            break
        page += 1
    return products


def _fetch_products(client: Any, path: str, *, page_size: int, max_pages: int) -> List[Dict[str, Any]]:
    """Backward-compatible fetch without attribute batching."""
    return _fetch_product_pages(client, path, page_size=page_size, max_pages=max_pages)


def _normalize_search_path(path: str) -> str:
    normalized = str(path or "").strip().lstrip("/").rstrip("/")
    if normalized.endswith("/search"):
        return normalized
    if normalized in {"attributes/product", "attributes"}:
        return "attributes/product/search"
    if normalized in {"products"}:
        return f"{normalized}/search"
    return normalized or "products/search"


def _items_from_payload(payload: Dict[str, Any]) -> List[Any]:
    for key in ("data", "items", "results", "products", "attributes"):
        value = payload.get(key)
        if isinstance(value, list):
            return value
        if isinstance(value, dict):
            nested = _items_from_payload(value)
            if nested:
                return nested
    return []


def _search_has_next_page(
    payload: Dict[str, Any],
    *,
    page: int,
    page_size: int,
    item_count: int,
) -> bool:
    if item_count >= page_size:
        return True
    pagination = _pagination_from_payload(payload)
    if not pagination:
        return False
    total = pagination.get("total_count") or pagination.get("count") or pagination.get("total")
    current_page = int(pagination.get("page") or page)
    effective_page_size = int(pagination.get("page_size") or page_size)
    if total is not None:
        try:
            return current_page * effective_page_size < int(total)
        except (TypeError, ValueError):
            return False
    return bool(pagination.get("has_next") or pagination.get("hasNextPage"))


def _pagination_from_payload(payload: Dict[str, Any]) -> Dict[str, Any]:
    direct = payload.get("pagination")
    if isinstance(direct, dict):
        return direct
    data = payload.get("data")
    if isinstance(data, dict) and isinstance(data.get("pagination"), dict):
        return data["pagination"]
    meta = payload.get("meta")
    if isinstance(meta, dict) and isinstance(meta.get("pagination"), dict):
        return meta["pagination"]
    return {}


def main() -> int:
    parser = argparse.ArgumentParser(description="Pull products from Plytix API")
    parser.add_argument("--source-label", default="plytix_api")
    parser.add_argument("--dry-run", action="store_true")
    args = parser.parse_args()

    result = run_plytix_pull(source_label=args.source_label, dry_run=args.dry_run)
    if result.get("status") != "ok":
        print(result.get("error", "Plytix pull failed"), file=sys.stderr)
        return 1
    print(
        "Plytix pull complete: "
        f"ingest_id={result.get('ingest_id')}, "
        f"input_rows={result.get('input_rows', 0)}, "
        f"distinct_skus={result.get('distinct_skus', 0)}, "
        f"inserted={result.get('inserted', 0)}, "
        f"unchanged={result.get('unchanged', 0)}"
    )
    return 0


if __name__ == "__main__":
    sys.exit(main())
