from ali_express_ds.ds.drop_shipping import getFreight, getProduct
from ali_express_ds.iop.ali_express_sdk import get_last_response


PRODUCT_RESPONSE_KEYS = (
    "aliexpress_ds_product_get_response",
    "aliexpress_ds_product_wholesale_get_response",
)
FREIGHT_RESPONSE_KEY = "aliexpress_ds_freight_query_response"


class AliExpressUpstreamError(Exception):
    """Raised when AliExpress returns an error or an unusable response."""


def importProduct(
    shipToCountry,
    productId,
    targetCurrency,
    targetLanguage,
    removePersonalBenefit=False,
    quantity=1,
    profitMargin=20,
):
    productJson = getProduct(
        shipToCountry,
        productId,
        targetCurrency,
        targetLanguage,
        removePersonalBenefit,
    )
    productSkus = _getProductSkus(productJson)
    freightResults = []
    freightLocale = targetLanguage

    for sku in productSkus:
        skuId = sku["sku_id"]
        freightJson = getFreight(
            quantity,
            shipToCountry,
            productId,
            skuId,
            targetLanguage,
            targetCurrency,
            freightLocale,
        )

        if freightJson is None:
            lastResponse = get_last_response()
            freightJson = getattr(lastResponse, "body", None)

        _getResponseResult(
            freightJson,
            (FREIGHT_RESPONSE_KEY,),
            f"freight request for SKU {skuId}",
        )

        freightResults.append(
            {
                "skuId": skuId,
                "freightJson": freightJson,
            }
        )

    return {
        "productJson": productJson,
        "freightResults": freightResults,
        "profitMargin": profitMargin,
    }


def _getProductSkus(productJson):
    result = _getResponseResult(
        productJson,
        PRODUCT_RESPONSE_KEYS,
        "product request",
    )
    skuContainer = result.get("ae_item_sku_info_dtos")

    if isinstance(skuContainer, dict):
        skuItems = skuContainer.get("ae_item_sku_info_d_t_o", [])
    elif isinstance(skuContainer, list):
        skuItems = skuContainer
    else:
        skuItems = []

    if isinstance(skuItems, dict):
        skuItems = [skuItems]
    elif not isinstance(skuItems, list):
        skuItems = []

    validSkus = []
    for sku in skuItems:
        if not isinstance(sku, dict):
            continue

        skuId = _normalizeSkuId(sku.get("sku_id"))
        if skuId is None:
            continue

        normalizedSku = sku.copy()
        normalizedSku["sku_id"] = skuId
        validSkus.append(normalizedSku)

    if not validSkus:
        raise AliExpressUpstreamError(
            "AliExpress product response did not contain a valid SKU id."
        )

    return validSkus


def _getResponseResult(payload, responseKeys, operation):
    if not isinstance(payload, dict):
        raise AliExpressUpstreamError(
            f"AliExpress {operation} returned an invalid response."
        )

    _raiseForUpstreamError(payload, operation)

    for responseKey in responseKeys:
        if responseKey not in payload:
            continue

        response = payload.get(responseKey)
        if not isinstance(response, dict):
            raise AliExpressUpstreamError(
                f"AliExpress {operation} returned an invalid response envelope."
            )

        _raiseForUpstreamError(response, operation)
        result = response.get("result")
        if not isinstance(result, dict):
            raise AliExpressUpstreamError(
                f"AliExpress {operation} returned an invalid result."
            )

        _raiseForUpstreamError(result, operation)
        return result

    raise AliExpressUpstreamError(
        f"AliExpress {operation} returned an unexpected response envelope."
    )


def _raiseForUpstreamError(payload, operation):
    errorResponse = payload.get("error_response")
    if isinstance(errorResponse, dict):
        code = errorResponse.get("code") or errorResponse.get("error_code")
        message = (
            errorResponse.get("message")
            or errorResponse.get("msg")
            or errorResponse.get("error_message")
        )
        raise AliExpressUpstreamError(
            _formatUpstreamError(operation, code, message)
        )

    code = payload.get("code")
    if code is None:
        code = payload.get("error_code")

    if _isErrorCode(code):
        message = (
            payload.get("message")
            or payload.get("msg")
            or payload.get("error_message")
        )
        raise AliExpressUpstreamError(
            _formatUpstreamError(operation, code, message)
        )

    success = payload.get("success")
    if success is None:
        success = payload.get("result_success")
    if success is False or str(success).strip().lower() == "false":
        message = (
            payload.get("message")
            or payload.get("msg")
            or payload.get("error_message")
        )
        raise AliExpressUpstreamError(
            _formatUpstreamError(operation, code, message)
        )


def _isErrorCode(code):
    if code is None:
        return False
    # AliExpress uses 0 for gateway success and 200 for a successful
    # freight-query business result.
    return str(code).strip().lower() not in (
        "",
        "0",
        "200",
        "success",
        "ok",
    )


def _formatUpstreamError(operation, code=None, message=None):
    safeCode = _safeErrorText(code, 50)
    safeMessage = _safeErrorText(message, 200)
    details = []

    if safeCode:
        details.append(f"code {safeCode}")
    if safeMessage:
        details.append(safeMessage)

    suffix = f" ({'; '.join(details)})" if details else ""
    return f"AliExpress {operation} failed{suffix}."


def _safeErrorText(value, maxLength):
    if value is None:
        return ""
    return " ".join(str(value).split())[:maxLength]


def _normalizeSkuId(value):
    if isinstance(value, bool) or not isinstance(value, (str, int)):
        return None

    skuId = str(value).strip()
    if not skuId or skuId == "0":
        return None
    return skuId
