from __future__ import annotations

import re
from typing import Any, Dict, FrozenSet, List, Mapping, Optional, Sequence, Tuple

from sqlalchemy import select
from sqlalchemy.orm import Session

from db.compat_connections import decode_compat_connection_id
from db.models import MagentoAttributeOptionRegistry, MagentoAttributeRegistry


# Internal/reference fields — shown last in the matrix, styled as metadata in the UI.
MATRIX_META_TAIL_KEYS: FrozenSet[str] = frozenset(
    {
        "plytix_product_id",
        "plytix_parent_id",
        "magento_product_id",
        "magento_attribute_set_id",
        "shopify_product_id",
        "shopify_variant_id",
        "product_id",
        "gtin",
        "num_variations",
    }
)

PLYTIX_FIELD_RENAMES = {
    "id": "plytix_product_id",
    "product_id": "plytix_product_id",
    "_parent_id": "plytix_parent_id",
    "parent_id": "plytix_parent_id",
}

MAGENTO_FIELD_RENAMES = {
    "id": "magento_product_id",
    "attribute_set_id": "magento_attribute_set_id",
}


def polish_plytix_matrix_fields(fields: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
    if not fields:
        return None
    out: Dict[str, Any] = {}
    for key, value in fields.items():
        if value in (None, ""):
            continue
        target = PLYTIX_FIELD_RENAMES.get(str(key), str(key))
        if target == "plytix_product_id" and "plytix_product_id" in out:
            continue
        if target == "plytix_parent_id" and "plytix_parent_id" in out:
            continue
        out[target] = value
    return out or None


def polish_magento_matrix_fields(
    fields: Optional[Dict[str, Any]],
    *,
    option_labels_by_code: Optional[Mapping[str, Mapping[int, str]]] = None,
    attribute_labels_by_code: Optional[Mapping[str, str]] = None,
) -> Optional[Dict[str, Any]]:
    if not fields:
        return None
    option_labels_by_code = option_labels_by_code or {}
    attribute_labels_by_code = attribute_labels_by_code or {}
    out: Dict[str, Any] = {}
    for key, value in fields.items():
        if value in (None, ""):
            continue
        text_key = str(key)
        target = MAGENTO_FIELD_RENAMES.get(text_key, text_key)
        display_value = _format_magento_matrix_value(
            target,
            value,
            option_labels_by_code=option_labels_by_code,
            attribute_labels_by_code=attribute_labels_by_code,
        )
        out[target] = display_value
    return out or None


def polish_shopify_matrix_fields(fields: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
    if not fields:
        return None
    return dict(fields)


def load_magento_matrix_label_indexes(
    session: Session,
    connection_id: Optional[int],
) -> Tuple[Dict[str, Dict[int, str]], Dict[str, str]]:
    """value_index -> option label, and attribute_code -> frontend label."""
    native_id = _magento_native_connection_id(connection_id)
    option_labels: Dict[str, Dict[int, str]] = {}
    attr_labels: Dict[str, str] = {}

    opt_stmt = select(
        MagentoAttributeOptionRegistry.attribute_code,
        MagentoAttributeOptionRegistry.value_index,
        MagentoAttributeOptionRegistry.option_label,
    )
    attr_stmt = select(
        MagentoAttributeRegistry.attribute_code,
        MagentoAttributeRegistry.frontend_label,
    )
    if native_id:
        opt_stmt = opt_stmt.where(MagentoAttributeOptionRegistry.connection_id == native_id)
        attr_stmt = attr_stmt.where(MagentoAttributeRegistry.connection_id == native_id)

    for code, value_index, label in session.execute(opt_stmt).all():
        attr_code = str(code or "").strip()
        if not attr_code:
            continue
        try:
            index = int(value_index)
        except (TypeError, ValueError):
            continue
        text = str(label or "").strip()
        if not text:
            continue
        option_labels.setdefault(attr_code, {})[index] = text

    for code, frontend_label in session.execute(attr_stmt).all():
        attr_code = str(code or "").strip()
        if not attr_code:
            continue
        label = str(frontend_label or "").strip()
        if label:
            attr_labels[attr_code] = label

    return option_labels, attr_labels


def sort_matrix_field_keys(keys: Sequence[str]) -> List[str]:
    body: List[str] = []
    tail: List[str] = []
    for key in keys:
        if key in MATRIX_META_TAIL_KEYS:
            tail.append(key)
        else:
            body.append(key)
    body.sort()
    tail.sort()
    return body + tail


def _format_magento_matrix_value(
    key: str,
    value: Any,
    *,
    option_labels_by_code: Mapping[str, Mapping[int, str]],
    attribute_labels_by_code: Mapping[str, str],
) -> Any:
    if not key.startswith("magento_"):
        return value
    attr_code = key[len("magento_") :]
    if not attr_code:
        return value
    text = str(value).strip()
    if not text or not re.fullmatch(r"-?\d+", text):
        return value
    try:
        index = int(text)
    except ValueError:
        return value
    label = (option_labels_by_code.get(attr_code) or {}).get(index)
    if not label:
        return value
    return f"{label} ({index})"


def _magento_native_connection_id(connection_id: Optional[int]) -> Optional[int]:
    if not connection_id:
        return None
    channel_type, native_id = decode_compat_connection_id(connection_id)
    if channel_type != "magento":
        return None
    return native_id
