"""
magento/deletion_service.py

Executes SKU removals in Magento (hard delete or disable).
Caller must pass action explicitly — no environment-based auto selection.
"""
from __future__ import annotations

import logging
import urllib.parse
from typing import Literal, Optional

logger = logging.getLogger(__name__)

MagentoRemovalAction = Literal["hard", "disable"]


def normalize_magento_removal_action(action: Optional[str]) -> MagentoRemovalAction:
    normalized = str(action or "").strip().lower()
    if normalized in {"hard", "delete", "hard_delete", "hard-delete"}:
        return "hard"
    if normalized in {"disable", "disabled", "soft"}:
        return "disable"
    raise ValueError("magento_action must be 'hard' or 'disable'")


def execute_deletion(
    api,  # MagentoRestClient instance
    sku: str,
    product_type: Optional[str],
    parent_sku: Optional[str],
    *,
    action: MagentoRemovalAction,
    remote_id: Optional[str] = None,
) -> str:
    """
    Execute delete/disable for one SKU. Returns human-readable result string.

    action:
      - disable: set status=2 (hidden/disabled in Magento)
      - hard: DELETE /V1/products/{sku}

    Raises RuntimeError when hard-delete fails and the product still exists.
    """
    parts: list[str] = []

    try:
        if product_type == "configurable":
            _unlink_all_children(api, sku, parts)
        elif product_type == "simple" and parent_sku:
            st, er = api.remove_configurable_child(parent_sku, sku)
            parts.append(
                f"unlinked from parent={parent_sku} HTTP {st}" + (f" [{er}]" if er else "")
            )

        if action == "disable":
            status, _ = api.put_product(sku, {"status": 2})
            parts.append(f"disabled status=2 HTTP {status}")
            if status not in (200, 201):
                raise RuntimeError("; ".join(parts))
        else:
            status, err = api.delete_product(sku)
            if status in (200, 204):
                parts.append(
                    "hard-deleted HTTP "
                    + str(status)
                    + (" (already absent)" if status == 204 else "")
                )
            else:
                still_there = False
                try:
                    still_there = bool(api.product_exists_by_sku_search(sku))
                except Exception:
                    still_there = True
                detail = f"hard-deleted HTTP {status}" + (f" [{err}]" if err else "")
                if remote_id:
                    detail += f" remote_id={remote_id}"
                parts.append(detail)
                if still_there:
                    raise RuntimeError(
                        "; ".join(parts)
                        + " — product still present via Magento search (slash SKU path likely)"
                    )
                parts.append("confirmed absent via search")

    except RuntimeError:
        raise
    except Exception as exc:
        parts.append(f"EXCEPTION: {exc}")
        logger.exception("execute_deletion error sku=%s", sku)
        raise RuntimeError("; ".join(parts)) from exc

    return "; ".join(parts) or "no action"


def _unlink_all_children(api, parent_sku: str, log: list) -> None:
    """Fetch children of a configurable and unlink each one."""
    encoded = urllib.parse.quote(parent_sku, safe="")
    status, children, err = api._client._request(
        "GET", f"configurable-products/{encoded}/children"
    )
    if status != 200 or not children:
        log.append(
            f"get-children HTTP {status}" + (f" [{err}]" if err else "")
        )
        return
    for child in children:
        child_sku = child.get("sku", "") if isinstance(child, dict) else ""
        if not child_sku:
            continue
        st, er = api.remove_configurable_child(parent_sku, child_sku)
        log.append(
            f"unlinked child={child_sku} HTTP {st}" + (f" [{er}]" if er else "")
        )
