from pathlib import Path
from types import SimpleNamespace

import pytest
from alembic.script import ScriptDirectory

from app import db_reset

ORG_ID = "org_dev"


@pytest.mark.parametrize(
    "url",
    [
        "postgresql+asyncpg://seoinsight:seoinsight@localhost:5432/data",
        "postgresql+asyncpg://seoinsight:seoinsight@127.0.0.1:5432/seoinsight_data_test",
        "postgresql+asyncpg://seoinsight:seoinsight@[::1]:5432/data",
    ],
)
def test_assert_local_accepts_local_hosts(url: str) -> None:
    db_reset.assert_local(url)


@pytest.mark.parametrize(
    "url",
    [
        "postgresql+asyncpg://seoinsight:secret@db.example.com:5432/data",
        "postgresql+asyncpg://seoinsight:secret@10.0.0.5:5432/data",
        "postgresql+asyncpg://seoinsight:secret@localhost.example.com:5432/data",
        "postgresql+asyncpg:///data",
        # asyncpg fait `opts.update(url.query)` : `host=` l'emporte sur l'hôte de l'URL.
        "postgresql+asyncpg://seoinsight:secret@localhost:5432/data?host=db.example.com",
        "postgresql+asyncpg://seoinsight:secret@localhost:5432/data?hostaddr=10.0.0.5",
    ],
)
def test_assert_local_rejects_remote_hosts(url: str) -> None:
    with pytest.raises(RuntimeError, match="Refus"):
        db_reset.assert_local(url)


def test_main_requires_org_id() -> None:
    # argparse : argument obligatoire absent → usage + code de sortie 2.
    with pytest.raises(SystemExit) as exc:
        db_reset.main([])
    assert exc.value.code == 2


def test_main_refuses_before_touching_database(
    monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
    remote = "postgresql+asyncpg://seoinsight:secret@db.example.com:5432/data"
    monkeypatch.setattr(db_reset, "get_settings", lambda: SimpleNamespace(database_url=remote))

    async def _must_not_run(_url: str) -> None:
        raise AssertionError("drop_public_schema ne doit pas être appelé")

    monkeypatch.setattr(db_reset, "drop_public_schema", _must_not_run)

    assert db_reset.main(["--org-id", ORG_ID]) == 1
    assert "db.example.com" in capsys.readouterr().err


def test_alembic_config_does_not_depend_on_the_cwd(
    monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
    monkeypatch.chdir(tmp_path)
    script_directory = ScriptDirectory.from_config(db_reset.alembic_config())
    assert Path(script_directory.dir) == Path(db_reset.__file__).resolve().parents[1] / "alembic"


def test_main_loads_migrations_before_dropping(monkeypatch: pytest.MonkeyPatch) -> None:
    # Une erreur de configuration Alembic doit tomber avant le DROP, jamais après.
    local = "postgresql+asyncpg://seoinsight:seoinsight@localhost:5432/data"
    monkeypatch.setattr(db_reset, "get_settings", lambda: SimpleNamespace(database_url=local))
    calls: list[str] = []

    class _FakeScriptDirectory:
        @staticmethod
        def from_config(_cfg: object) -> object:
            calls.append("script_directory")
            return object()

    async def _drop(_url: str) -> None:
        calls.append("drop")

    def _upgrade(_cfg: object, _revision: str) -> None:
        calls.append("upgrade")

    monkeypatch.setattr(db_reset, "ScriptDirectory", _FakeScriptDirectory)
    monkeypatch.setattr(db_reset, "drop_public_schema", _drop)
    monkeypatch.setattr(db_reset.command, "upgrade", _upgrade)

    assert db_reset.main(["--org-id", ORG_ID]) == 0
    assert calls == ["script_directory", "drop", "upgrade"]
