"""Inspect Magento product values across admin/store-view scopes.

Useful when a PUT succeeds but the Magento admin grid or storefront still shows
old product names/url keys because a store-view scoped value or stale duplicate
product is being displayed.
"""

from __future__ import annotations

import argparse
import json
from typing import Any, Dict, Iterable, List, Optional

from sqlalchemy import select

from db.models import (
    ChannelSkuMapping,
    MagentoCatalogState,
    MagentoConnection,
    MagentoStoreRegistry,
)
from db.session import get_session
from magento.magento_api import MagentoRestClient
from magento.oauth_client import MagentoOAuthClient, build_magento_oauth_kwargs


def _api_for_connection(conn: MagentoConnection, *, rest_base_path: str) -> MagentoRestClient:
    return MagentoRestClient(
        MagentoOAuthClient(**build_magento_oauth_kwargs(conn, rest_base_path_override=rest_base_path))
    )


def _custom_attr(product: Optional[Dict[str, Any]], code: str) -> Optional[Any]:
    if not isinstance(product, dict):
        return None
    for attr in product.get("custom_attributes") or []:
        if isinstance(attr, dict) and str(attr.get("attribute_code") or "") == code:
            return attr.get("value")
    return None


def _category_ids(product: Optional[Dict[str, Any]]) -> List[Any]:
    if not isinstance(product, dict):
        return []
    links = ((product.get("extension_attributes") or {}).get("category_links") or [])
    return [link.get("category_id") for link in links if isinstance(link, dict)]


def _summarize_product(product: Optional[Dict[str, Any]]) -> Dict[str, Any]:
    if product is None:
        return {"found": False}
    return {
        "found": True,
        "id": product.get("id"),
        "sku": product.get("sku"),
        "name": product.get("name"),
        "type_id": product.get("type_id"),
        "status": product.get("status"),
        "visibility": product.get("visibility"),
        "updated_at": product.get("updated_at"),
        "url_key": _custom_attr(product, "url_key"),
        "meta_title": _custom_attr(product, "meta_title"),
        "category_ids": _category_ids(product),
    }


def _lookup_product(api: MagentoRestClient, *, sku: str, remote_id: Optional[str] = None) -> Dict[str, Any]:
    product = api.get_product(sku)
    if product is not None:
        summary = _summarize_product(product)
        summary["lookup_method"] = "direct_sku"
        return summary

    status, items, err = api.search_products(sku=sku, page_size=5)
    if status == 200 and items:
        for item in items:
            if str((item or {}).get("sku") or "").strip() == sku:
                summary = _summarize_product(item)
                summary["lookup_method"] = "search_sku"
                return summary

    if str(remote_id or "").strip().isdigit():
        status, items, err = api.search_products(entity_id=int(str(remote_id).strip()), page_size=5)
        if status == 200 and items:
            summary = _summarize_product(items[0])
            summary["lookup_method"] = "search_entity_id"
            return summary

    return {
        "found": False,
        "lookup_method": "not_found",
        "lookup_error": err if "err" in locals() else None,
    }


def _scope_paths(session, conn: MagentoConnection) -> List[Dict[str, str]]:
    base_path = (conn.rest_base_path or "V1").strip("/") or "V1"
    scopes: List[Dict[str, str]] = [{"label": "admin", "rest_base_path": base_path}]
    if conn.store_code:
        scopes.append(
            {
                "label": f"connection_store:{conn.store_code}",
                "rest_base_path": f"{conn.store_code}/{base_path}",
            }
        )
    for store in session.scalars(
        select(MagentoStoreRegistry)
        .where(MagentoStoreRegistry.connection_id == conn.id)
        .order_by(MagentoStoreRegistry.is_default.desc(), MagentoStoreRegistry.store_code)
    ).all():
        code = str(store.store_code or "").strip()
        if not code or code == "admin":
            continue
        scopes.append({"label": f"registry_store:{code}", "rest_base_path": f"{code}/{base_path}"})

    deduped: List[Dict[str, str]] = []
    seen = set()
    for scope in scopes:
        key = scope["rest_base_path"]
        if key in seen:
            continue
        seen.add(key)
        deduped.append(scope)
    return deduped


def _local_rows(session, connection_id: int, skus: Iterable[str]) -> Dict[str, Any]:
    wanted = {str(s).strip() for s in skus if str(s).strip()}
    if not wanted:
        return {}
    catalog = [
        {
            "sku": row.sku,
            "magento_product_id": row.magento_product_id,
            "updated_at_magento": row.updated_at_magento,
            "last_pulled_at": row.last_pulled_at,
        }
        for row in session.scalars(
            select(MagentoCatalogState)
            .where(MagentoCatalogState.connection_id == connection_id)
            .where(MagentoCatalogState.sku.in_(sorted(wanted)))
            .order_by(MagentoCatalogState.sku)
        ).all()
    ]
    mappings = [
        {
            "id": row.id,
            "master_sku": row.master_sku,
            "channel_sku": row.channel_sku,
            "remote_id": row.remote_id,
            "status": row.mapping_status,
            "match_source": row.match_source,
        }
        for row in session.scalars(
            select(ChannelSkuMapping)
            .where(ChannelSkuMapping.connection_id == connection_id)
            .where(
                (ChannelSkuMapping.master_sku.in_(sorted(wanted)))
                | (ChannelSkuMapping.channel_sku.in_(sorted(wanted)))
            )
            .order_by(ChannelSkuMapping.master_sku, ChannelSkuMapping.channel_sku)
        ).all()
    ]
    return {"catalog_state": catalog, "channel_sku_mapping": mappings}


def run_debug(connection_id: int, skus: List[str]) -> Dict[str, Any]:
    with get_session() as session:
        conn = session.get(MagentoConnection, connection_id)
        if conn is None:
            return {"status": "failed", "error": f"Magento connection {connection_id} not found"}
        scopes = _scope_paths(session, conn)
        local = _local_rows(session, connection_id, skus)
        remote_ids = {}
        for row in local.get("channel_sku_mapping") or []:
            master = str(row.get("master_sku") or "").strip()
            channel = str(row.get("channel_sku") or "").strip()
            rid = str(row.get("remote_id") or "").strip()
            if master and rid:
                remote_ids[master] = rid
            if channel and rid:
                remote_ids[channel] = rid
        for row in local.get("catalog_state") or []:
            sku = str(row.get("sku") or "").strip()
            rid = str(row.get("magento_product_id") or "").strip()
            if sku and rid and sku not in remote_ids:
                remote_ids[sku] = rid
        inspected = []
        for scope in scopes:
            api = _api_for_connection(conn, rest_base_path=scope["rest_base_path"])
            products = {}
            for sku in skus:
                products[sku] = _lookup_product(api, sku=sku, remote_id=remote_ids.get(sku))
            inspected.append({**scope, "products": products})
        return {
            "status": "ok",
            "connection": {
                "id": conn.id,
                "store_base_url": conn.store_base_url,
                "magento_api_base_url": conn.magento_api_base_url,
                "rest_base_path": conn.rest_base_path,
                "store_code": conn.store_code,
            },
            "skus": skus,
            "local": local,
            "scopes": inspected,
        }


def main() -> int:
    parser = argparse.ArgumentParser(description="Debug Magento product admin/store-view scoped values")
    parser.add_argument("--connection-id", type=int, required=True)
    parser.add_argument("--sku", action="append", dest="skus", required=True)
    args = parser.parse_args()

    result = run_debug(args.connection_id, [str(s).strip() for s in args.skus if str(s).strip()])
    print(json.dumps(result, indent=2, default=str))
    return 0 if result.get("status") == "ok" else 1


if __name__ == "__main__":
    raise SystemExit(main())
