"""Delete remote Magento categories / Shopify collections for taxonomy links."""

from __future__ import annotations

from typing import Any, Dict, List, Optional, Tuple

from sqlalchemy.orm import Session

from db.models import MasterTaxonomyChannelLink, ShopifyConnection


def _native_connection_id(channel_type: str, connection_id: Optional[int]) -> Optional[int]:
    if connection_id is None:
        return None
    from db.compat_connections import resolve_native_connection_id

    return resolve_native_connection_id(channel_type, int(connection_id))


def resolve_channel_clients_for_purge(
    session: Session,
    *,
    magento_connection_id: Optional[int] = None,
    shopify_connection_id: Optional[int] = None,
) -> Tuple[Optional[Any], Optional[Any]]:
    magento_api = None
    shopify_client = None
    if magento_connection_id is not None:
        from db.collection_landing_push import _build_magento_api

        magento_api, _ = _build_magento_api(session, magento_connection_id)
    shopify_native = _native_connection_id("shopify", shopify_connection_id)
    if shopify_native is not None:
        from shopify.connections import build_client

        conn = session.get(ShopifyConnection, int(shopify_native))
        if conn is not None:
            shopify_client = build_client(conn)
    return magento_api, shopify_client


def purge_remote_taxonomy_links(
    session: Session,
    links: List[MasterTaxonomyChannelLink],
    *,
    magento_api: Optional[Any] = None,
    shopify_client: Optional[Any] = None,
    dry_run: bool = False,
) -> Dict[str, Any]:
    """Best-effort remote cleanup for deactivated taxonomy channel links."""
    stats: Dict[str, Any] = {
        "dry_run": dry_run,
        "magento_deleted": 0,
        "shopify_deleted": 0,
        "skipped": 0,
        "errors": [],
        "actions": [],
    }
    for link in links:
        channel = str(link.channel_code or "").strip().lower()
        remote_id = str(link.remote_id or "").strip()
        if not remote_id:
            stats["skipped"] += 1
            continue
        try:
            if channel == "magento":
                action = _purge_magento_category(magento_api, remote_id, dry_run=dry_run)
                if action.get("status") in {"deleted", "would_delete"}:
                    stats["magento_deleted"] += 1
                else:
                    stats["skipped"] += 1
                stats["actions"].append(action)
            elif channel == "shopify":
                action = _purge_shopify_collection(shopify_client, remote_id, dry_run=dry_run)
                if action.get("status") in {"deleted", "would_delete"}:
                    stats["shopify_deleted"] += 1
                else:
                    stats["skipped"] += 1
                stats["actions"].append(action)
            else:
                stats["skipped"] += 1
        except Exception as exc:
            stats["errors"].append(
                {
                    "channel_code": channel,
                    "remote_id": remote_id,
                    "error": str(exc),
                }
            )
    return stats


def _sort_magento_targets(targets: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
    """Delete deepest categories first so Magento allows removal of leaf nodes."""
    return sorted(
        targets,
        key=lambda row: (
            0 if str(row.get("channel_code") or "").lower() == "magento" else 1,
            -int(row.get("level") or 0),
            str(row.get("remote_id") or ""),
        ),
    )


# Purge outcomes that mean the remote row is gone (either just now or previously).
_PURGE_SUCCESS_STATUSES = {"deleted", "would_delete", "already_deleted"}


def purge_remote_catalog_targets(
    *,
    targets: List[Dict[str, Any]],
    magento_api: Optional[Any] = None,
    shopify_client: Optional[Any] = None,
    dry_run: bool = False,
    session: Optional[Session] = None,
) -> Dict[str, Any]:
    """Delete remote catalog rows identified by channel_code + remote_id.

    When ``session`` is provided, stale taxonomy channel links and listing-path
    targets pointing at a purged (or already-deleted) remote id are deactivated so
    repeat runs don't retry dead ids.
    """
    stats: Dict[str, Any] = {
        "dry_run": dry_run,
        "magento_deleted": 0,
        "shopify_deleted": 0,
        "already_deleted": 0,
        "links_deactivated": 0,
        "skipped": 0,
        "errors": [],
        "actions": [],
    }
    seen: set[tuple[str, str]] = set()
    for target in _sort_magento_targets(targets):
        channel = str(target.get("channel_code") or "").strip().lower()
        remote_id = str(target.get("remote_id") or "").strip()
        dedupe_key = (channel, remote_id)
        if not channel or not remote_id or dedupe_key in seen:
            stats["skipped"] += 1
            continue
        seen.add(dedupe_key)
        try:
            if channel == "magento":
                action = _purge_magento_category(magento_api, remote_id, dry_run=dry_run)
                action.update(
                    {
                        "source": target.get("source") or target.get("sources"),
                        "path_slug": target.get("path_slug"),
                        "path_names": target.get("path_names"),
                        "name": target.get("name"),
                        "level": target.get("level"),
                        "purge_reason": target.get("purge_reason"),
                    }
                )
                if action.get("status") in _PURGE_SUCCESS_STATUSES:
                    stats["magento_deleted"] += 1
                else:
                    stats["skipped"] += 1
                stats["actions"].append(action)
            elif channel == "shopify":
                action = _purge_shopify_collection(shopify_client, remote_id, dry_run=dry_run)
                action.update(
                    {
                        "source": target.get("source") or target.get("sources"),
                        "path_slug": target.get("path_slug"),
                        "name": target.get("name"),
                    }
                )
                if action.get("status") in _PURGE_SUCCESS_STATUSES:
                    stats["shopify_deleted"] += 1
                else:
                    stats["skipped"] += 1
                stats["actions"].append(action)
            else:
                stats["skipped"] += 1
                continue

            if action.get("status") == "already_deleted":
                stats["already_deleted"] += 1
            if (
                session is not None
                and not dry_run
                and action.get("status") in _PURGE_SUCCESS_STATUSES
                and action.get("status") != "would_delete"
            ):
                stats["links_deactivated"] += _deactivate_stale_remote_links(
                    session, channel_code=channel, remote_id=remote_id
                )
        except Exception as exc:
            stats["errors"].append(
                {
                    "channel_code": channel,
                    "remote_id": remote_id,
                    "path_slug": target.get("path_slug"),
                    "name": target.get("name"),
                    "error": str(exc),
                }
            )
    return stats


def _deactivate_stale_remote_links(
    session: Session,
    *,
    channel_code: str,
    remote_id: str,
) -> int:
    """Deactivate taxonomy links and listing-path targets for a deleted remote id."""
    from sqlalchemy import select

    from db.models import ChannelListingPathTarget

    deactivated = 0
    links = session.scalars(
        select(MasterTaxonomyChannelLink)
        .where(MasterTaxonomyChannelLink.channel_code == channel_code)
        .where(MasterTaxonomyChannelLink.remote_id == remote_id)
        .where(MasterTaxonomyChannelLink.is_active.is_(True))
    ).all()
    for link in links:
        link.is_active = False
        deactivated += 1

    targets = session.scalars(
        select(ChannelListingPathTarget)
        .where(ChannelListingPathTarget.channel_code == channel_code)
        .where(ChannelListingPathTarget.remote_id == remote_id)
        .where(ChannelListingPathTarget.is_active.is_(True))
    ).all()
    for target in targets:
        target.is_active = False
        deactivated += 1

    return deactivated


def _purge_magento_category(api: Optional[Any], remote_id: str, *, dry_run: bool) -> Dict[str, Any]:
    if not remote_id.isdigit():
        return {"channel": "magento", "remote_id": remote_id, "status": "skipped", "reason": "invalid_id"}
    if api is None:
        return {"channel": "magento", "remote_id": remote_id, "status": "skipped", "reason": "no_api"}
    category_id = int(remote_id)
    if dry_run:
        return {"channel": "magento", "remote_id": remote_id, "status": "would_delete"}
    search_status, items = api.search_categories(category_id=category_id)
    if search_status == 200 and not items:
        return {"channel": "magento", "remote_id": remote_id, "status": "already_deleted"}
    if search_status != 200 or items is None:
        raise RuntimeError(f"Magento category lookup failed before delete: HTTP {search_status}")
    status, body, err = api.delete_category(category_id)
    if status == 404:
        return {"channel": "magento", "remote_id": remote_id, "status": "already_deleted"}
    if status not in (200, 201, 204):
        raise RuntimeError(f"Magento delete category failed: HTTP {status} {body or err}")
    return {"channel": "magento", "remote_id": remote_id, "status": "deleted"}


def _is_shopify_not_found_error(message: str) -> bool:
    text = str(message or "").lower()
    return "does not exist" in text or "was not found" in text or "not found" in text


def _purge_shopify_collection(client: Optional[Any], remote_id: str, *, dry_run: bool) -> Dict[str, Any]:
    if client is None:
        return {"channel": "shopify", "remote_id": remote_id, "status": "skipped", "reason": "no_client"}
    if dry_run:
        return {"channel": "shopify", "remote_id": remote_id, "status": "would_delete"}
    from shopify.collection_sync import delete_shopify_collection

    try:
        delete_shopify_collection(client, collection_id=remote_id)
    except RuntimeError as exc:
        if _is_shopify_not_found_error(str(exc)):
            return {"channel": "shopify", "remote_id": remote_id, "status": "already_deleted"}
        raise
    return {"channel": "shopify", "remote_id": remote_id, "status": "deleted"}
