"""Réglages d'organisation : `GET`/`PATCH /settings/org` (spec Imports §3.10, §6).

Une seule valeur en v1, la **source préférée** — celle qui passe en **tête** des observations
de chaque métrique, sans en masquer aucune (spec 2026-09-01 §3.4). Elle n'élit plus rien et ne
déplace donc plus le résultat d'un filtre ; ce module ne teste que l'écriture du réglage, dont
le contrat n'a pas bougé. Elle est validée contre le **registre d'adaptateurs** et non contre
une contrainte en base : la liste des sources est du code et bouge à chaque tranche.
"""

import uuid
from typing import Any

import pytest
from httpx import AsyncClient
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.keywords.models import OrgSettings
from tests.conftest import Fixture

Headers = dict[str, str]


@pytest.fixture
def headers(auth_headers: Any, sandbox: Fixture) -> Headers:
    return auth_headers(sandbox.org_id)


async def test_reading_does_not_create_a_row(
    client: AsyncClient, headers: Headers, sandbox: Fixture, db: AsyncSession
) -> None:
    response = await client.get("/settings/org", headers=headers)
    assert response.status_code == 200
    assert response.json() == {"preferred_source": None, "updated_at": None}
    # Une lecture ne doit rien écrire : l'absence de réglage *est* le repli de §3.3.
    assert await db.scalar(select(OrgSettings).where(OrgSettings.org_id == sandbox.org_id)) is None


async def test_patch_sets_and_clears_the_preferred_source(
    client: AsyncClient, headers: Headers
) -> None:
    set_ = await client.patch(
        "/settings/org", headers=headers, json={"preferred_source": "semrush_magic"}
    )
    assert set_.status_code == 200
    assert set_.json()["preferred_source"] == "semrush_magic"
    assert set_.json()["updated_at"] is not None
    assert (await client.get("/settings/org", headers=headers)).json()["preferred_source"] == (
        "semrush_magic"
    )

    cleared = await client.patch("/settings/org", headers=headers, json={"preferred_source": None})
    assert cleared.json()["preferred_source"] is None


async def test_patch_without_the_field_changes_nothing(
    client: AsyncClient, headers: Headers, sandbox: Fixture, db: AsyncSession
) -> None:
    """Sémantique PATCH : champ **absent** = inchangé, champ à `null` = effacé.

    Confondre les deux est anodin avec un seul réglage et devient une perte de données
    silencieuse dès qu'un second entrera dans cette table.
    """
    await client.patch("/settings/org", headers=headers, json={"preferred_source": "semrush_magic"})
    untouched = await client.patch("/settings/org", headers=headers, json={})
    assert untouched.status_code == 200
    assert untouched.json()["preferred_source"] == "semrush_magic"
    assert (await client.get("/settings/org", headers=headers)).json()["preferred_source"] == (
        "semrush_magic"
    )


async def test_empty_patch_does_not_create_a_row(
    client: AsyncClient, headers: Headers, sandbox: Fixture, db: AsyncSession
) -> None:
    response = await client.patch("/settings/org", headers=headers, json={})
    assert response.status_code == 200
    assert response.json() == {"preferred_source": None, "updated_at": None}
    assert await db.scalar(select(OrgSettings).where(OrgSettings.org_id == sandbox.org_id)) is None


@pytest.mark.parametrize(
    "body",
    [
        {"preferred_source": "ahrefs"},  # absente du registre d'adaptateurs (§3.10)
        {"preferred_source": ""},
        {"preferred": "semrush_magic"},  # extra='forbid'
    ],
)
async def test_patch_validation(
    client: AsyncClient, headers: Headers, body: dict[str, Any]
) -> None:
    response = await client.patch("/settings/org", headers=headers, json=body)
    assert response.status_code == 422
    assert response.headers["content-type"].startswith("application/problem+json")


async def test_settings_are_per_organisation(
    client: AsyncClient, headers: Headers, auth_headers: Any
) -> None:
    await client.patch("/settings/org", headers=headers, json={"preferred_source": "generic_csv"})
    stranger = auth_headers(f"org_{uuid.uuid4().hex}")
    assert (await client.get("/settings/org", headers=stranger)).json()["preferred_source"] is None


async def test_settings_require_a_token(client: AsyncClient) -> None:
    assert (await client.get("/settings/org")).status_code == 401
