import pytest
from fastapi.testclient import TestClient

from app.main import app

client = TestClient(app)
TENANT = "org_8c32bf3e-6a18-4739-9b1c-94c0cf11125f"


def test_connect_needs_only_a_base_url(monkeypatch):
    # No field map, no url_template, no currency. Everything else is inferred by
    # the pipeline on first sync.
    from app.api import sources
    saved = {}
    monkeypatch.setattr(sources, "upsert_source",
                        lambda *a, **kw: saved.update({"args": a}))

    resp = client.post("/sources/http", json={
        "tenant_id": TENANT, "base_url": "https://dummyjson.com/products"})

    assert resp.status_code == 200
    assert resp.json()["external_ref"] == "dummyjson.com"
    assert saved, "the source must be stored"


def test_connect_rejects_an_internal_base_url():
    # https, deliberately: an http:// case here would now be turned away on
    # scheme alone and never exercise the resolved-address check this test
    # is actually meant to cover.
    resp = client.post("/sources/http", json={
        "tenant_id": TENANT, "base_url": "https://127.0.0.1:8001/products"})
    assert resp.status_code == 400


def test_connect_rejects_a_non_http_scheme():
    resp = client.post("/sources/http", json={
        "tenant_id": TENANT, "base_url": "file:///etc/passwd"})
    assert resp.status_code == 400


def test_connect_rejects_cloud_metadata():
    # https, for the same reason as the internal-address case above.
    resp = client.post("/sources/http", json={
        "tenant_id": TENANT, "base_url": "https://169.254.169.254/latest/meta-data/"})
    assert resp.status_code == 400


def test_optional_overrides_are_accepted(monkeypatch):
    # A merchant whose API defeats inference can still supply values by hand;
    # none of them is required.
    from app.api import sources
    saved = {}
    monkeypatch.setattr(sources, "upsert_source",
                        lambda t, k, r, config, creds: saved.update(config))

    client.post("/sources/http", json={
        "tenant_id": TENANT, "base_url": "https://dummyjson.com/products",
        "currency": "USD",
        "url_template": "https://dummyjson.com/products/{external_id}"})

    assert saved["currency"] == "USD"
    assert saved["url_template"].endswith("{external_id}")


# --- the website source ------------------------------------------------------

def test_connecting_a_website_needs_only_a_url(monkeypatch):
    from app.api import sources
    saved = {}
    monkeypatch.setattr(sources, "upsert_source",
                        lambda *a, **kw: saved.update({"args": a}))

    resp = client.post("/sources/website", json={
        "tenant_id": TENANT, "url": "https://dummyjson.com/products/shirt"})

    assert resp.status_code == 200
    body = resp.json()
    assert body["kind"] == "crawl"
    assert body["external_ref"] == "dummyjson.com"
    assert saved, "the source must be stored"


def test_a_website_without_a_currency_reports_it_in_needs(monkeypatch):
    # Same rule as a product API: the source works without one, and a wrong
    # guess would mis-price the whole catalogue invisibly.
    from app.api import sources
    monkeypatch.setattr(sources, "upsert_source", lambda *a, **kw: None)
    resp = client.post("/sources/website", json={
        "tenant_id": TENANT, "url": "https://dummyjson.com"})
    assert resp.json()["needs"] == ["currency"]


def test_a_website_with_a_currency_needs_nothing(monkeypatch):
    from app.api import sources
    monkeypatch.setattr(sources, "upsert_source", lambda *a, **kw: None)
    resp = client.post("/sources/website", json={
        "tenant_id": TENANT, "url": "https://dummyjson.com",
        "currency": "USD"})
    assert resp.json()["needs"] == []


def test_a_website_source_stores_no_credentials(monkeypatch):
    # A public page needs none, and an empty envelope would imply an account.
    from app.api import sources
    captured = {}
    monkeypatch.setattr(sources, "upsert_source",
                        lambda t, k, r, config, creds:
                        captured.update({"credentials": creds}))

    client.post("/sources/website", json={
        "tenant_id": TENANT, "url": "https://dummyjson.com"})

    assert captured["credentials"] == {}


def test_connecting_a_website_rejects_an_internal_url():
    resp = client.post("/sources/website", json={
        "tenant_id": TENANT, "url": "https://127.0.0.1:8001/"})
    assert resp.status_code == 400


def test_connecting_a_website_rejects_a_non_https_scheme():
    resp = client.post("/sources/website", json={
        "tenant_id": TENANT, "url": "http://dummyjson.com"})
    assert resp.status_code == 400


def test_connecting_a_website_needs_a_url():
    resp = client.post("/sources/website", json={"tenant_id": TENANT})
    assert resp.status_code == 422


def test_connect_reports_that_a_currency_is_needed(monkeypatch):
    # No currency supplied and none inferred -- the response has to say so,
    # or a merchant is left wondering why nothing is ever recommended.
    from app.api import sources
    monkeypatch.setattr(sources, "upsert_source", lambda *a, **kw: None)

    resp = client.post("/sources/http", json={
        "tenant_id": TENANT, "base_url": "https://dummyjson.com/products"})

    assert resp.json()["needs"] == ["currency"]


def test_connect_with_a_currency_reports_nothing_needed(monkeypatch):
    from app.api import sources
    monkeypatch.setattr(sources, "upsert_source", lambda *a, **kw: None)

    resp = client.post("/sources/http", json={
        "tenant_id": TENANT, "base_url": "https://dummyjson.com/products",
        "currency": "USD"})

    assert resp.json()["needs"] == []


def test_list_sources_reports_needs_per_source(monkeypatch):
    from app.api import sources
    monkeypatch.setattr(sources, "get_sources", lambda tenant_id: [
        {"kind": "http_api", "external_ref": "dummyjson.com",
         "config": {"base_url": "https://dummyjson.com/products"},
         "status": "active", "connected_at": None, "last_synced_at": None},
        {"kind": "shopify", "external_ref": "s.myshopify.com",
         "config": {"currency_code": "USD"}, "status": "active",
         "connected_at": None, "last_synced_at": None},
    ])
    monkeypatch.setattr(sources, "get_integration", lambda *a: None)

    body = client.get("/sources", params={"tenant_id": TENANT}).json()

    by_ref = {s["external_ref"]: s for s in body["sources"]}
    assert by_ref["dummyjson.com"]["needs"] == ["currency"]
    assert by_ref["s.myshopify.com"]["needs"] == []


def test_list_sources_includes_the_shopify_connection():
    resp = client.get("/sources", params={"tenant_id": TENANT})
    assert resp.status_code == 200
    kinds = {s["kind"] for s in resp.json()["sources"]}
    assert "shopify" in kinds


def test_list_never_returns_credentials():
    body = client.get("/sources", params={"tenant_id": TENANT}).text
    for leak in ("shpat_", "credentials", "accessToken", "api_key"):
        assert leak not in body


def test_delete_unknown_source_404s():
    resp = client.delete("/sources/http_api/not-connected.example")
    assert resp.status_code in (404, 422)


def test_tenant_id_is_required():
    assert client.get("/sources").status_code == 422
