import pytest
from flask_migrate import downgrade, upgrade

from app import create_app, db
from app.config import check_access_config
from app.models import Account
from tests.conftest import add_account, login, make_config


@pytest.fixture
def locked_app(tmp_path):
    application = create_app(make_config(tmp_path, TIKTOK_ALLOWED_ACCOUNTS=["union_allowed", "open_allowed"]))
    with application.app_context():
        upgrade()
    yield application
    with application.app_context():
        db.session.remove()
        downgrade(revision="base")
        db.engine.dispose()


def _login_as(client, monkeypatch, open_id, union_id):
    monkeypatch.setattr("app.auth.exchange_code", lambda *a, **k: {
        "access_token": "a", "refresh_token": "r", "open_id": open_id, "expires_in": 86400, "scope": "user.info.basic"})
    monkeypatch.setattr("app.auth.get_user_info", lambda token: {"display_name": "Someone", "union_id": union_id})
    client.get("/auth/start")
    with client.session_transaction() as s:
        state = s["oauth_state"]
    return client.get(f"/auth/callback?code=c&state={state}", follow_redirects=True)


def test_listed_union_id_signs_in(locked_app, monkeypatch):
    client = locked_app.test_client()
    r = _login_as(client, monkeypatch, "open_x", "union_allowed")
    assert "Connected Someone" in r.get_data(as_text=True)
    with locked_app.app_context():
        assert Account.query.filter_by(open_id="open_x").one().union_id == "union_allowed"


def test_unlisted_account_is_refused_and_not_stored(locked_app, monkeypatch):
    client = locked_app.test_client()
    r = _login_as(client, monkeypatch, "open_y", "union_other")
    text = r.get_data(as_text=True)
    assert "not allowed" in text
    assert "union_other" in text
    with locked_app.app_context():
        assert Account.query.count() == 0


def test_session_of_a_removed_account_is_closed(locked_app):
    account_id = add_account(locked_app, open_id="open_removed")
    client = locked_app.test_client()
    login(client, account_id)
    r = client.get("/dashboard")
    assert r.status_code == 302
    assert "/login" in r.headers["Location"]


def test_production_refuses_an_empty_allowlist():
    with pytest.raises(RuntimeError):
        check_access_config({"FLASK_ENV": "production", "TIKTOK_ALLOWED_ACCOUNTS": []})
    check_access_config({"FLASK_ENV": "production", "TIKTOK_ALLOWED_ACCOUNTS": ["*"]})
    check_access_config({"FLASK_ENV": "development", "TIKTOK_ALLOWED_ACCOUNTS": []})
