"""Integration coverage for the business-scoped products/inventory foundation."""

import uuid

import pytest
from sqlalchemy import select

from app.core.security import hash_password
from app.db.session import SessionLocal
from app.models.branch import Branch
from app.models.inventory import BranchInventory, InventoryTransfer
from app.models.membership import Membership
from app.models.product import Product
from app.models.user import User


pytest_plugins = ("test_workspace",)


def headers(user):
    from app.core.security import create_access_token

    return {
        "Authorization": f"Bearer {create_access_token(str(user.id))}"
    }


def workspace(client, user, name="Store"):
    response = client.post(
        "/api/businesses/onboarding",
        json={
            "name": name,
            "main_branch_name": "Main",
            "country": "Nigeria",
            "currency": "NGN",
            "default_language": "en",
        },
        headers=headers(user),
    )

    assert response.status_code == 201, response.text

    return response.json()


def another_business(client, user, name):
    return client.post(
        "/api/businesses",
        json={
            "name": name,
            "main_branch_name": "Main",
            "country": "Nigeria",
            "currency": "NGN",
            "default_language": "en",
        },
        headers={
            **headers(user),
            "Idempotency-Key": uuid.uuid4().hex,
        },
    ).json()


def product(
    client,
    user,
    business,
    name="P",
    **extra,
):
    body = {
        "name": name,
        "product_type": "SINGLE",
        "selling_price": "1",
        **extra,
    }

    return client.post(
        f"/api/businesses/{business}/products",
        json=body,
        headers=headers(user),
    )


def test_category_unit_product_types_and_business_isolation(
    client,
    verified_user,
):
    first = workspace(client, verified_user)
    business = first["business"]["id"]

    category = client.post(
        f"/api/businesses/{business}/categories",
        json={"name": "Goods"},
        headers=headers(verified_user),
    )

    assert category.status_code == 201

    assert client.post(
        f"/api/businesses/{business}/categories",
        json={"name": "Goods"},
        headers=headers(verified_user),
    ).status_code == 409

    unit = client.post(
        f"/api/businesses/{business}/units",
        json={
            "name": "Piece",
            "symbol": "pc",
        },
        headers=headers(verified_user),
    )

    assert unit.status_code == 201

    unit_id = unit.json()["id"]

    for kind in (
        "SINGLE",
        "PIECES_PACK",
        "VARIATION",
        "MULTI_LEVEL",
    ):
        body = {
            "name": kind,
            "product_type": kind,
            "selling_price": "12.50",
            "base_unit_id": unit_id,
            "track_stock": kind != "SINGLE",
            "expiry_date": "2030-01-01",
            "image_url": "/images/item.png",
        }

        if kind in (
            "PIECES_PACK",
            "MULTI_LEVEL",
        ):
            body["conversions"] = [
                {
                    "unit_id": unit_id,
                    "quantity_in_base": "12",
                }
            ]

        if kind == "VARIATION":
            body["variants"] = [
                {
                    "name": "Red silk",
                    "attributes": {
                        "Fabric": "Silk",
                        "Pattern": "Striped",
                    },
                    "sku": "V-SKU",
                    "barcode": "V-BAR",
                    "selling_price": "15",
                }
            ]

        response = client.post(
            f"/api/businesses/{business}/products",
            json=body,
            headers=headers(verified_user),
        )

        assert response.status_code == 201

    products = client.get(
        f"/api/businesses/{business}/products",
        headers=headers(verified_user),
    ).json()

    assert len(products) == 4

    variation_product = next(
        item
        for item in products
        if item["product_type"] == "VARIATION"
    )

    detail = client.get(
        f"/api/businesses/{business}/products/{variation_product['id']}",
        headers=headers(verified_user),
    ).json()

    assert (
        detail["variants"][0]["attributes"]["Fabric"]
        == "Silk"
    )


def test_stock_adjustment_and_transfer_invariants(
    client,
    verified_user,
):
    data = workspace(
        client,
        verified_user,
    )

    business = data["business"]["id"]
    branch = data["accessible_branches"][0]["id"]

    created_product = client.post(
        f"/api/businesses/{business}/products",
        json={
            "name": "Stock",
            "product_type": "SINGLE",
            "selling_price": "1",
        },
        headers=headers(verified_user),
    ).json()

    adjust = (
        f"/api/businesses/{business}"
        f"/branches/{branch}/stock-adjustments"
    )

    assert client.post(
        adjust,
        json={
            "product_id": created_product["id"],
            "quantity_changed": "10",
            "reason": "RESTOCK",
        },
        headers=headers(verified_user),
    ).status_code == 201

    assert client.post(
        adjust,
        json={
            "product_id": created_product["id"],
            "quantity_changed": "-11",
            "reason": "LOST",
        },
        headers=headers(verified_user),
    ).status_code == 422

    history = client.get(
        f"/api/businesses/{business}"
        f"/branches/{branch}/stock-history",
        headers=headers(verified_user),
    ).json()

    assert history[0]["previous_quantity"] == 0
    assert history[0]["reason"] == "RESTOCK"


@pytest.mark.parametrize(
    "reason",
    [
        "RESTOCK",
        "RETURN",
        "LOST",
        "DAMAGE",
        "DEDUCTION",
    ],
)
def test_each_adjustment_reason_records_real_history(
    client,
    verified_user,
    reason,
):
    data = workspace(
        client,
        verified_user,
    )

    business = data["business"]["id"]
    branch = data["accessible_branches"][0]["id"]

    created_product = client.post(
        f"/api/businesses/{business}/products",
        json={
            "name": "P",
            "product_type": "SINGLE",
            "selling_price": "1",
        },
        headers=headers(verified_user),
    ).json()

    endpoint = (
        f"/api/businesses/{business}"
        f"/branches/{branch}/stock-adjustments"
    )

    assert client.post(
        endpoint,
        json={
            "product_id": created_product["id"],
            "quantity_changed": "10",
            "reason": "RESTOCK",
            "comment": "initial",
        },
        headers=headers(verified_user),
    ).status_code == 201

    delta = (
        "2"
        if reason in {"RESTOCK", "RETURN"}
        else "-2"
    )

    response = client.post(
        endpoint,
        json={
            "product_id": created_product["id"],
            "quantity_changed": delta,
            "reason": reason,
            "comment": "audit",
        },
        headers=headers(verified_user),
    )

    assert response.status_code == 201

    row = client.get(
        f"/api/businesses/{business}"
        f"/products/{created_product['id']}/stock-history",
        headers=headers(verified_user),
    ).json()[0]

    assert row["adjustment"]["reason"] == reason
    assert row["adjustment"]["comment"] == "audit"
    assert row["user"]["id"] == str(verified_user.id)


def test_cross_business_categories_units_and_product_inputs_are_isolated(
    client,
    verified_user,
):
    business_a = workspace(
        client,
        verified_user,
        "Alpha",
    )["business"]["id"]

    business_b = another_business(
        client,
        verified_user,
        "Beta",
    )["business"]["id"]

    for business in (
        business_a,
        business_b,
    ):
        response = client.post(
            f"/api/businesses/{business}/categories",
            json={"name": "General"},
            headers=headers(verified_user),
        )

        assert response.status_code == 201

    categories_a = client.get(
        f"/api/businesses/{business_a}/categories",
        headers=headers(verified_user),
    ).json()

    assert len(categories_a) == 1

    unit_a = client.post(
        f"/api/businesses/{business_a}/units",
        json={
            "name": "Pack",
            "symbol": "pk",
        },
        headers=headers(verified_user),
    ).json()

    assert client.post(
        f"/api/businesses/{business_b}/units",
        json={
            "name": "Pack",
            "symbol": "pk",
        },
        headers=headers(verified_user),
    ).status_code == 201

    assert product(
        client,
        verified_user,
        business_b,
        "wrong",
        base_unit_id=unit_a["id"],
    ).status_code == 422


def test_service_sku_barcode_and_detail_response(
    client,
    verified_user,
):
    business_a = workspace(
        client,
        verified_user,
        "Alpha",
    )["business"]["id"]

    business_b = another_business(
        client,
        verified_user,
        "Beta",
    )["business"]["id"]

    unit = client.post(
        f"/api/businesses/{business_a}/units",
        json={
            "name": "Piece",
            "symbol": "pc",
        },
        headers=headers(verified_user),
    ).json()

    category = client.post(
        f"/api/businesses/{business_a}/categories",
        json={"name": "Service"},
        headers=headers(verified_user),
    ).json()

    response = product(
        client,
        verified_user,
        business_a,
        "Service",
        track_stock=False,
        sku="S",
        barcode="B",
        category_id=category["id"],
        base_unit_id=unit["id"],
        conversions=[
            {
                "unit_id": unit["id"],
                "quantity_in_base": "1",
            }
        ],
    )

    assert response.status_code == 201

    assert product(
        client,
        verified_user,
        business_a,
        "dup",
        sku="S",
    ).status_code == 409

    assert product(
        client,
        verified_user,
        business_a,
        "dupbar",
        barcode="B",
    ).status_code == 409

    assert product(
        client,
        verified_user,
        business_b,
        "same",
        sku="S",
        barcode="B",
    ).status_code == 201

    detail = client.get(
        f"/api/businesses/{business_a}"
        f"/products/{response.json()['id']}",
        headers=headers(verified_user),
    ).json()

    assert detail["product"]["track_stock"] is False
    assert detail["category"]["name"] == "Service"
    assert detail["base_unit"]["symbol"] == "pc"
    assert len(detail["conversions"]) == 1


def test_inventory_variants_branches_transfer_and_negative_rules(
    client,
    verified_user,
):
    data = workspace(
        client,
        verified_user,
    )

    business = data["business"]["id"]
    source = data["accessible_branches"][0]["id"]

    db = SessionLocal()

    destination = Branch(
        business_id=uuid.UUID(business),
        name="Warehouse",
        is_active=True,
    )

    db.add(destination)
    db.commit()
    db.refresh(destination)
    db.close()

    created_product = product(
        client,
        verified_user,
        business,
        "V",
        product_type="VARIATION",
        variants=[
            {
                "name": "A",
                "attributes": {"Tone": "A"},
            },
            {
                "name": "B",
                "attributes": {"Tone": "B"},
            },
        ],
    ).json()

    variants = client.get(
        f"/api/businesses/{business}"
        f"/products/{created_product['id']}",
        headers=headers(verified_user),
    ).json()["variants"]

    adjustment_endpoint = (
        f"/api/businesses/{business}"
        f"/branches/{source}/stock-adjustments"
    )

    for variant in variants:
        response = client.post(
            adjustment_endpoint,
            json={
                "product_id": created_product["id"],
                "variant_id": variant["id"],
                "quantity_changed": "10",
                "reason": "RESTOCK",
            },
            headers=headers(verified_user),
        )

        assert response.status_code == 201

    inventory = client.get(
        f"/api/businesses/{business}"
        f"/branches/{source}/inventory",
        headers=headers(verified_user),
    ).json()

    assert len(inventory) == 2

    for reason in (
        "DAMAGE",
        "DEDUCTION",
    ):
        response = client.post(
            adjustment_endpoint,
            json={
                "product_id": created_product["id"],
                "variant_id": variants[0]["id"],
                "quantity_changed": "-11",
                "reason": reason,
            },
            headers=headers(verified_user),
        )

        assert response.status_code == 422

    transfer = {
        "source_branch_id": source,
        "destination_branch_id": str(destination.id),
        "product_id": created_product["id"],
        "variant_id": variants[0]["id"],
        "quantity": "3",
    }

    assert client.post(
        f"/api/businesses/{business}/inventory-transfers",
        json=transfer,
        headers=headers(verified_user),
    ).status_code == 201

    destination_inventory = client.get(
        f"/api/businesses/{business}"
        f"/branches/{destination.id}/inventory",
        headers=headers(verified_user),
    ).json()

    assert len(destination_inventory) == 1

    assert client.post(
        f"/api/businesses/{business}/inventory-transfers",
        json={
            **transfer,
            "source_branch_id": source,
            "destination_branch_id": source,
        },
        headers=headers(verified_user),
    ).status_code == 422


def test_unauthorized_and_aggregated_endpoints_cannot_cross_business(
    client,
    verified_user,
):
    data = workspace(
        client,
        verified_user,
        "Owner Store",
    )

    business = data["business"]["id"]
    branch = data["accessible_branches"][0]["id"]

    created_product = product(
        client,
        verified_user,
        business,
        "Private",
    ).json()

    db = SessionLocal()

    outsider = User(
        full_name="Outsider",
        email=f"out-{uuid.uuid4().hex}@x.test",
        password_hash=hash_password("Password123!"),
        is_email_verified=True,
    )

    db.add(outsider)
    db.commit()
    db.refresh(outsider)

    try:
        outsider_headers = headers(outsider)

        adjustment_endpoint = (
            f"/api/businesses/{business}"
            f"/branches/{branch}/stock-adjustments"
        )

        assert client.get(
            f"/api/businesses/{business}/products",
            headers=outsider_headers,
        ).status_code == 404

        assert client.get(
            f"/api/businesses/{business}"
            f"/products/{created_product['id']}",
            headers=outsider_headers,
        ).status_code == 404

        assert client.post(
            adjustment_endpoint,
            json={
                "product_id": created_product["id"],
                "quantity_changed": "1",
                "reason": "RESTOCK",
            },
            headers=outsider_headers,
        ).status_code == 404

        assert client.get(
            f"/api/businesses/{business}"
            f"/products/{created_product['id']}/inventory",
            headers=outsider_headers,
        ).status_code == 404

        assert client.get(
            f"/api/businesses/{business}"
            f"/products/{created_product['id']}/stock-history",
            headers=outsider_headers,
        ).status_code == 404

    finally:
        db.execute(
            __import__("sqlalchemy").delete(User).where(
                User.id == outsider.id
            )
        )

        db.commit()
        db.close()


def test_inactive_membership_is_denied_product_and_inventory_access(
    client,
    verified_user,
):
    data = workspace(
        client,
        verified_user,
        "Inactive Store",
    )

    business = data["business"]["id"]
    branch = data["accessible_branches"][0]["id"]

    created_product = product(
        client,
        verified_user,
        business,
        "Private",
    ).json()

    db = SessionLocal()

    membership = db.scalar(
        select(Membership).where(
            Membership.business_id == uuid.UUID(business),
            Membership.user_id == verified_user.id,
        )
    )

    membership.is_active = False

    db.commit()
    db.close()

    user_headers = headers(verified_user)

    assert client.get(
        f"/api/businesses/{business}/products",
        headers=user_headers,
    ).status_code == 404

    assert client.get(
        f"/api/businesses/{business}"
        f"/products/{created_product['id']}",
        headers=user_headers,
    ).status_code == 404

    assert client.post(
        f"/api/businesses/{business}"
        f"/branches/{branch}/stock-adjustments",
        json={
            "product_id": created_product["id"],
            "quantity_changed": "1",
            "reason": "RESTOCK",
        },
        headers=user_headers,
    ).status_code == 404


def test_overstock_and_cross_business_transfers_leave_inventory_unchanged(
    client,
    verified_user,
):
    data = workspace(
        client,
        verified_user,
        "Alpha Store",
    )

    business = data["business"]["id"]
    source = data["accessible_branches"][0]["id"]

    db = SessionLocal()

    destination = Branch(
        business_id=uuid.UUID(business),
        name="Dest",
        is_active=True,
    )

    db.add(destination)
    db.commit()
    db.refresh(destination)
    db.close()

    created_product = product(
        client,
        verified_user,
        business,
        "Transfer",
    ).json()

    adjustment_endpoint = (
        f"/api/businesses/{business}"
        f"/branches/{source}/stock-adjustments"
    )

    client.post(
        adjustment_endpoint,
        json={
            "product_id": created_product["id"],
            "quantity_changed": "2",
            "reason": "RESTOCK",
        },
        headers=headers(verified_user),
    )

    payload = {
        "source_branch_id": source,
        "destination_branch_id": str(destination.id),
        "product_id": created_product["id"],
        "quantity": "3",
    }

    response = client.post(
        f"/api/businesses/{business}/inventory-transfers",
        json=payload,
        headers=headers(verified_user),
    )

    assert response.status_code == 422

    inventory = client.get(
        f"/api/businesses/{business}"
        f"/branches/{source}/inventory",
        headers=headers(verified_user),
    ).json()

    assert inventory[0]["quantity"] == 2


def test_inactive_membership_cannot_transfer_inventory(
    client,
    verified_user,
):
    data = workspace(
        client,
        verified_user,
        "Inactive Transfer",
    )

    business_id = data["business"]["id"]
    source_branch = data["accessible_branches"][0]["id"]

    created_product = product(
        client,
        verified_user,
        business_id,
        "Transfer Product",
    ).json()

    db = SessionLocal()

    destination = Branch(
        business_id=uuid.UUID(business_id),
        name="Destination",
        is_active=True,
    )

    db.add(destination)
    db.commit()
    db.refresh(destination)

    membership = db.scalar(
        select(Membership).where(
            Membership.business_id == uuid.UUID(
                business_id
            ),
            Membership.user_id == verified_user.id,
        )
    )

    membership.is_active = False

    db.commit()

    destination_id = str(destination.id)

    db.close()

    response = client.post(
        f"/api/businesses/{business_id}/inventory-transfers",
        json={
            "source_branch_id": source_branch,
            "destination_branch_id": destination_id,
            "product_id": created_product["id"],
            "quantity": "1",
        },
        headers=headers(verified_user),
    )

    assert response.status_code == 404


def test_wrong_business_cannot_access_product_inventory_or_history(
    client,
    verified_user,
):
    first = workspace(
        client,
        verified_user,
        "Business A",
    )

    second = another_business(
        client,
        verified_user,
        "Business B",
    )

    business_a = first["business"]["id"]
    business_b = second["business"]["id"]

    created_product = product(
        client,
        verified_user,
        business_a,
        "Business A Product",
    ).json()

    inventory_response = client.get(
        f"/api/businesses/{business_b}"
        f"/products/{created_product['id']}/inventory",
        headers=headers(verified_user),
    )

    history_response = client.get(
        f"/api/businesses/{business_b}"
        f"/products/{created_product['id']}/stock-history",
        headers=headers(verified_user),
    )

    assert inventory_response.status_code == 404
    assert history_response.status_code == 404