from __future__ import annotations

from datetime import datetime, timezone
import re

import pytest
from fastapi.testclient import TestClient
from sqlalchemy import create_engine, event, select
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy.pool import StaticPool

from db.base import Base
from db.models import MagentoConnection, MasterBrand
from db.models_v2 import V2BrandCollection, V2Category, V2Product
from test_v2_event_queueing import LEGACY_TABLES, _create_v2_test_schema
from v2_api import get_db


@pytest.fixture()
def v2_session() -> Session:
    engine = create_engine(
        "sqlite://",
        connect_args={"check_same_thread": False},
        poolclass=StaticPool,
    )

    @event.listens_for(engine, "connect")
    def _sqlite_pragma(dbapi_connection, connection_record):
        cursor = dbapi_connection.cursor()
        cursor.execute("PRAGMA foreign_keys=ON")
        cursor.close()
        dbapi_connection.create_function(
            "regexp_replace",
            4,
            lambda value, pattern, replacement, flags: re.sub(pattern, replacement, value or ""),
            deterministic=True,
        )
        dbapi_connection.create_function("now", 0, lambda: datetime.now(timezone.utc).isoformat())

    Base.metadata.create_all(engine, tables=LEGACY_TABLES)
    session = sessionmaker(bind=engine, expire_on_commit=False)()
    _create_v2_test_schema(session)
    try:
        yield session
    finally:
        session.close()
        engine.dispose()


@pytest.fixture()
def v2_client(v2_session: Session):
    from main_v2 import app

    def _override_db():
        yield v2_session

    app.dependency_overrides[get_db] = _override_db
    try:
        yield TestClient(app)
    finally:
        app.dependency_overrides.clear()


def _seed_catalog(session: Session) -> dict[str, int]:
    brand = MasterBrand(code="castle-craft", name="Castle Craft", is_active=True, aliases={"items": []})
    session.add(brand)
    hub = V2Category(name="Kitchen Cabinets", node_type="hub", parent_id=None, level=1, normalized_name="kitchen cabinets")
    session.add(hub)
    session.flush()
    category = V2Category(
        name="Anna Snow White",
        node_type="category",
        parent_id=int(hub.id),
        level=2,
        normalized_name="anna snow white",
    )
    collection = V2Category(
        name="Anna Oak",
        node_type="collection",
        parent_id=int(hub.id),
        level=2,
        normalized_name="anna oak",
    )
    session.add_all([category, collection])
    session.flush()
    empty_hub = V2Category(name="Bath", node_type="hub", parent_id=None, level=1, normalized_name="bath")
    session.add(empty_hub)
    session.flush()
    active = V2Product(
        sku="SKU-ACTIVE",
        title="Active Door",
        product_type="simple",
        lifecycle_status="active",
        source="manual",
        shopping_l1_category_id=int(hub.id),
        shopping_l2_category_id=int(category.id),
        primary_category_id=int(category.id),
        attributes={"product_family": "Doors"},
    )
    archived = V2Product(
        sku="SKU-ARCHIVED",
        title="Old Door",
        product_type="simple",
        lifecycle_status="archived",
        source="manual",
        shopping_l1_category_id=int(hub.id),
        attributes={},
    )
    pending = V2Product(
        sku="SKU-PENDING",
        title="Pending Door",
        product_type="simple",
        lifecycle_status="draft",
        source="manual",
        shopping_l1_category_id=int(empty_hub.id),
        attributes={},
    )
    session.add_all([active, pending, archived])
    session.flush()
    session.add(MagentoConnection(
        store_base_url="https://store.example",
        magento_api_base_url="https://store.example/rest",
        environment="production",
        consumer_key="ck",
        consumer_secret="super-secret",
        access_token="token",
        access_token_secret="token-secret",
        status="active",
        signature_method="HMAC-SHA256",
    ))
    session.commit()
    return {
        "brand_id": int(brand.id),
        "hub_id": int(hub.id),
        "empty_hub_id": int(empty_hub.id),
        "category_id": int(category.id),
        "collection_id": int(collection.id),
    }


def test_dashboard_renders_outbound_connections(v2_client: TestClient, v2_session: Session):
    _seed_catalog(v2_session)
    summary = v2_client.get("/api/v2/dashboard/summary").json()
    assert summary["connection_counts"]["total"] == 1
    assert summary["connection_counts"]["magento"] == 1
    assert len(summary["by_connection"]) == 1
    card = summary["by_connection"][0]
    assert card["channel_type"] == "magento"
    assert card["status"] == "active"
    assert card["environment"] == "production"
    listed = v2_client.get("/api/v2/connections").json()["connections"][0]
    assert "super-secret" not in str(listed)
    assert listed["secrets_configured"]["consumer_secret"] is True
    assert "consumer_secret" not in (listed.get("config") or {})


def test_brand_collection_assignment_round_trip(v2_client: TestClient, v2_session: Session):
    ids = _seed_catalog(v2_session)
    updated = v2_client.patch(
        f"/api/v2/brands/{ids['brand_id']}",
        json={"collection_ids": [ids["collection_id"]]},
    )
    assert updated.status_code == 200
    brand = updated.json()["brand"]
    assert brand["collection_count"] == 1
    assert brand["collections"][0]["name"] == "Anna Oak"
    assert brand["collections"][0]["hub_name"] == "Kitchen Cabinets"
    reopened = v2_client.get(f"/api/v2/brands/{ids['brand_id']}").json()
    assert [item["id"] for item in reopened["collections"]] == [ids["collection_id"]]
    cleared = v2_client.patch(f"/api/v2/brands/{ids['brand_id']}", json={"collection_ids": []})
    assert cleared.json()["brand"]["collections"] == []


def test_brand_updates_auto_sync_magento_brand_registry(v2_client: TestClient, v2_session: Session, monkeypatch: pytest.MonkeyPatch):
    ids = _seed_catalog(v2_session)
    calls: list[dict] = []

    def _fake_sync(_session, connection, *, replace_existing=True):
        calls.append({"connection_id": int(connection["id"]), "replace_existing": replace_existing})
        return {
            "status": 200,
            "replace_existing": replace_existing,
            "brands_sent": 1,
            "collections_sent": 1,
            "result": {"ok": True},
            "error": None,
            "ok": True,
        }

    monkeypatch.setattr("magento.brand_registry_sync.sync_brand_registry_for_connection", _fake_sync)

    response = v2_client.patch(
        f"/api/v2/brands/{ids['brand_id']}",
        json={"collection_ids": [ids["collection_id"]]},
    )

    assert response.status_code == 200
    body = response.json()
    assert calls == [{"connection_id": 1, "replace_existing": True}]
    assert body["magento_brand_registry_sync"]["attempted"] == 1
    assert body["magento_brand_registry_sync"]["synced"] == 1
    assert body["magento_brand_registry_sync"]["failed"] == 0
    assert body["magento_brand_registry_sync"]["results"][0]["connection_id"] == 1
    assert body["magento_brand_registry_sync"]["results"][0]["ok"] is True


def test_product_default_filter_excludes_archive_and_exposes_hub_category(v2_client: TestClient, v2_session: Session):
    ids = _seed_catalog(v2_session)
    payload = v2_client.get("/api/v2/product-snapshots").json()
    skus = [row["sku"] for row in payload["rows"]]
    assert "SKU-ACTIVE" in skus
    assert "SKU-PENDING" in skus
    assert "SKU-ARCHIVED" not in skus
    active = next(row for row in payload["rows"] if row["sku"] == "SKU-ACTIVE")
    assert active["fields"]["hub"] == "Kitchen Cabinets"
    assert active["fields"]["category"] == "Anna Snow White"
    assert active["fields"]["status"] == "active"

    archived = v2_client.get("/api/v2/product-snapshots", params={"lifecycle_status": "archive"}).json()
    assert [row["sku"] for row in archived["rows"]] == ["SKU-ARCHIVED"]
    all_rows = v2_client.get("/api/v2/product-snapshots", params={"lifecycle_status": "all"}).json()
    assert {row["sku"] for row in all_rows["rows"]} >= {"SKU-ACTIVE", "SKU-PENDING", "SKU-ARCHIVED"}

    created = v2_client.post(
        "/api/v2/products",
        json={
            "sku": "SKU-NEW",
            "title": "New Door",
            "product_status": "pending",
            "hub_id": ids["empty_hub_id"],
            "collection_ids": [],
        },
    )
    assert created.status_code == 200
    body = created.json()
    assert body["product_status"] == "pending"
    assert body["hub_id"] == ids["empty_hub_id"]
    assert body["collections"] == []


def test_collection_must_use_hub_and_ignores_level(v2_client: TestClient, v2_session: Session):
    ids = _seed_catalog(v2_session)
    missing = v2_client.post("/api/v2/collections", json={"name": "Orphan", "created_by": "test"})
    assert missing.status_code == 400
    nested = v2_client.post(
        "/api/v2/collections",
        json={"name": "Nested", "parent_id": ids["category_id"], "created_by": "test"},
    )
    assert nested.status_code == 400
    created = v2_client.post(
        "/api/v2/collections",
        json={"name": "Bath Line", "hub_id": ids["empty_hub_id"], "level": 99, "created_by": "test"},
    )
    assert created.status_code == 200
    row = v2_session.get(V2Category, created.json()["collection"]["id"])
    assert int(row.parent_id) == ids["empty_hub_id"]
    assert int(row.level) == 2

    options = v2_client.get("/api/v2/taxonomy/options", params={"node_type": "hub"}).json()["items"]
    assert {item["name"] for item in options} >= {"Kitchen Cabinets", "Bath"}
    collections = v2_client.get("/api/v2/taxonomy/options", params={"node_type": "collection", "parent_id": ids["empty_hub_id"]}).json()["items"]
    assert collections[0]["name"] == "Bath Line"
