"""
Acceptance tests for Magento sync hardening (MSI, media, data hash).

F1–F5 from sync hardening spec.
"""

from __future__ import annotations

import os
from unittest.mock import MagicMock

import pytest

from magento.inventory_service import ensure_simple_sku_inventory
from magento.sync_hashes import compute_data_hash
from magento.payload_mapper import snapshot_to_magento_product
from magento.sync_planner import plan_incremental_sync, ACTION_UPSERT_PRODUCT, ACTION_UPSERT_MEDIA


def test_f1_new_simple_sku_no_source_items_assigns_all_sources():
    """F1: New simple SKU with no source-items → all sources, qty=1, status=1."""
    from settings import MagentoMsiConfig
    api = MagicMock()
    api.get_inventory_sources.return_value = (200, [
        {"source_code": "default", "enabled": 1},
        {"source_code": "warehouse2", "enabled": 1},
    ], None)
    api.get_source_items_for_skus.return_value = (200, {"NEW-SKU": []}, None)
    api.post_source_items.return_value = (200, None)
    cfg = MagentoMsiConfig(
        default_qty_if_missing=1,
        force_zero_qty_to_default=True,
        assign_all_sources_if_none=True,
        assign_all_sources_if_partial=True,
        preserve_existing_positive_qty=True,
    )
    result = ensure_simple_sku_inventory(
        api, 1, "NEW-SKU",
        source_codes=["default", "warehouse2"],
        msi_config=cfg,
    )
    assert result.success
    assert result.mode == "created_all"
    assert len(result.after_source_items) == 2
    for si in result.after_source_items:
        assert si.get("quantity") == 1
        assert si.get("status") == 1


def test_f2_existing_sku_zero_qty_force_patch():
    """F2: Existing SKU with one source qty=0, FORCE_ZERO_QTY_TO_DEFAULT → qty=1."""
    from settings import MagentoMsiConfig
    api = MagicMock()
    api.get_inventory_sources.return_value = (200, [{"source_code": "default"}], None)
    api.get_source_items_for_skus.return_value = (200, {
        "EXISTING": [{"source_code": "default", "quantity": 0, "status": 0}],
    }, None)
    api.post_source_items.return_value = (200, None)
    cfg = MagentoMsiConfig(
        default_qty_if_missing=1,
        force_zero_qty_to_default=True,
        assign_all_sources_if_none=True,
        assign_all_sources_if_partial=True,
        preserve_existing_positive_qty=True,
    )
    result = ensure_simple_sku_inventory(
        api, 1, "EXISTING",
        source_codes=["default"],
        msi_config=cfg,
    )
    assert result.success
    assert result.mode == "patched_zero_qty"
    assert "default" in (result.sources_updated or [])


def test_f3_existing_sku_positive_qty_preserved():
    """F3: Existing SKU with qty=5, PRESERVE_EXISTING_POSITIVE_QTY → qty stays 5."""
    from settings import MagentoMsiConfig
    api = MagicMock()
    api.get_inventory_sources.return_value = (200, [{"source_code": "default"}, {"source_code": "w2"}], None)
    api.get_source_items_for_skus.return_value = (200, {
        "NETSUITE-SKU": [{"source_code": "default", "quantity": 5, "status": 1}],
    }, None)
    api.post_source_items.return_value = (200, None)
    cfg = MagentoMsiConfig(
        default_qty_if_missing=1,
        force_zero_qty_to_default=True,
        assign_all_sources_if_none=True,
        assign_all_sources_if_partial=True,
        preserve_existing_positive_qty=True,
    )
    result = ensure_simple_sku_inventory(
        api, 1, "NETSUITE-SKU",
        source_codes=["default", "w2"],
        msi_config=cfg,
    )
    assert result.success
    # Adds w2 at qty=1, does not change default qty=5
    assert "w2" in (result.sources_added or [])
    posted = api.post_source_items.call_args[0][0]
    for si in posted:
        if si["source_code"] == "default":
            assert si["quantity"] == 5
        else:
            assert si["quantity"] == 1


def test_f4_media_requires_product_planner_adds_upsert_product():
    """F4: Media for SKU absent in Magento → planner adds UPSERT_PRODUCT first."""
    row = {
        "sku": "NO-PRODUCT",
        "name": "Test",
        "product_type": "simple",
        "base_image": "https://example.com/img.jpg",
    }
    state = {"data_hash": "x", "images_hash": "old", "relations_hash": "z"}
    # No catalog_state = product does not exist
    plan = plan_incremental_sync(1, [row], {"NO-PRODUCT": state})
    actions = [a.action for a in plan.actions]
    # MEDIA_REQUIRE_PRODUCT_EXISTENCE adds UPSERT_PRODUCT before UPSERT_MEDIA
    assert ACTION_UPSERT_PRODUCT in actions
    assert ACTION_UPSERT_MEDIA in actions
    # Product must come before media in plan
    idx_prod = next(i for i, a in enumerate(plan.actions) if a.action == ACTION_UPSERT_PRODUCT)
    idx_media = next(i for i, a in enumerate(plan.actions) if a.action == ACTION_UPSERT_MEDIA)
    assert idx_prod < idx_media


def test_always_enabled_ignores_product_online():
    """When MAGENTO_ALWAYS_ENABLED_IGNORE_FEED=true, product_online=0 does not disable."""
    import os
    os.environ["MAGENTO_ALWAYS_ENABLED_IGNORE_FEED"] = "true"
    try:
        from magento.payload_mapper import snapshot_to_magento_product
        row = {"sku": "OFFLINE-SKU", "name": "Test", "product_type": "simple", "product_online": "0"}
        payload = snapshot_to_magento_product(row, is_new=False)
        assert payload["status"] == 1
    finally:
        os.environ.pop("MAGENTO_ALWAYS_ENABLED_IGNORE_FEED", None)


def test_f5_data_hash_includes_managed_custom_attrs():
    """F5: Change managed custom attr → data_hash changes, UPSERT_PRODUCT planned."""
    row1 = {
        "sku": "ATTR-SKU",
        "name": "Product",
        "product_type": "simple",
        "width": "30",
        "height": "20",
    }
    payload1 = snapshot_to_magento_product(row1, is_new=False)
    h1 = compute_data_hash(row1, payload1)

    row2 = {**row1, "width": "32"}
    payload2 = snapshot_to_magento_product(row2, is_new=False)
    h2 = compute_data_hash(row2, payload2)

    assert h1 != h2, "width change should change data_hash"

    state = {"data_hash": h1, "images_hash": "x", "relations_hash": "y"}
    plan = plan_incremental_sync(1, [row2], {"ATTR-SKU": state})
    assert any(a.action == ACTION_UPSERT_PRODUCT for a in plan.actions)
