from urllib.parse import parse_qs, urlparse

from fastapi.testclient import TestClient

from services.auth_service.app import app
from services.shared.security import decode_app_token


def test_login_success():
    client = TestClient(app)
    response = client.post('/auth/login', json={'username': 'admin', 'password': 'admin123'})
    assert response.status_code == 200
    body = response.json()
    assert body['access_token']
    assert body['role'] == 'admin'
    assert body['auth_source'] == 'local'
    claims = decode_app_token(body['access_token'])
    assert claims['username'] == 'admin'
    assert claims['role'] == 'admin'
    assert claims['auth_source'] == 'local'


def test_create_user_as_admin():
    client = TestClient(app)
    response = client.post(
        '/users',
        json={
            'username': 'newoperator',
            'password': 'password1',
            'full_name': 'New Operator',
            'role': 'operator',
        },
        headers={'X-User': 'admin', 'X-Role': 'admin'},
    )
    assert response.status_code == 200
    assert response.json()['username'] == 'newoperator'


def test_operator_cannot_create_user():
    client = TestClient(app)
    response = client.post(
        '/users',
        json={
            'username': 'blockeduser',
            'password': 'password1',
            'full_name': 'Blocked User',
            'role': 'operator',
        },
        headers={'X-User': 'operator', 'X-Role': 'operator'},
    )
    assert response.status_code == 403


def test_admin_can_update_user():
    client = TestClient(app)
    created = client.post(
        '/users',
        json={
            'username': 'updatableuser',
            'password': 'password1',
            'full_name': 'Updatable User',
            'role': 'operator',
        },
        headers={'X-User': 'admin', 'X-Role': 'admin'},
    )
    assert created.status_code == 200

    updated = client.patch(
        f"/users/{created.json()['user_id']}",
        json={
            'full_name': 'Updated User',
            'role': 'analyst',
            'password': 'password2',
        },
        headers={'X-User': 'admin', 'X-Role': 'admin'},
    )
    assert updated.status_code == 200
    assert updated.json()['full_name'] == 'Updated User'
    assert updated.json()['role'] == 'analyst'


def test_supervisor_cannot_update_user():
    client = TestClient(app)
    created = client.post(
        '/users',
        json={
            'username': 'guardeduser',
            'password': 'password1',
            'full_name': 'Guarded User',
            'role': 'operator',
        },
        headers={'X-User': 'admin', 'X-Role': 'admin'},
    )
    assert created.status_code == 200

    denied = client.patch(
        f"/users/{created.json()['user_id']}",
        json={'full_name': 'Should Not Update'},
        headers={'X-User': 'supervisor', 'X-Role': 'supervisor'},
    )
    assert denied.status_code == 403


def test_admin_can_delete_user():
    client = TestClient(app)
    created = client.post(
        '/users',
        json={
            'username': 'deletecandidate',
            'password': 'password1',
            'full_name': 'Delete Candidate',
            'role': 'operator',
        },
        headers={'X-User': 'admin', 'X-Role': 'admin'},
    )
    assert created.status_code == 200

    deleted = client.delete(
        f"/users/{created.json()['user_id']}",
        headers={'X-User': 'admin', 'X-Role': 'admin'},
    )
    assert deleted.status_code == 200
    assert deleted.json()['deleted'] is True

    lookup = client.get(
        f"/users/{created.json()['user_id']}",
        headers={'X-User': 'admin', 'X-Role': 'admin'},
    )
    assert lookup.status_code == 404


def test_admin_cannot_delete_current_user():
    client = TestClient(app)

    users = client.get('/users', headers={'X-User': 'admin', 'X-Role': 'admin'})
    assert users.status_code == 200
    admin_user = next(item for item in users.json() if item['username'] == 'admin')

    deleted = client.delete(
        f"/users/{admin_user['user_id']}",
        headers={'X-User': 'admin', 'X-Role': 'admin'},
    )
    assert deleted.status_code == 409


def test_supervisor_cannot_delete_user():
    client = TestClient(app)
    created = client.post(
        '/users',
        json={
            'username': 'nodeletebysupervisor',
            'password': 'password1',
            'full_name': 'No Delete',
            'role': 'operator',
        },
        headers={'X-User': 'admin', 'X-Role': 'admin'},
    )
    assert created.status_code == 200

    denied = client.delete(
        f"/users/{created.json()['user_id']}",
        headers={'X-User': 'supervisor', 'X-Role': 'supervisor'},
    )
    assert denied.status_code == 403


def test_oidc_config_reflects_disabled_state(monkeypatch):
    monkeypatch.setenv('OIDC_ENABLED', '0')
    client = TestClient(app)

    response = client.get('/auth/oidc/config')

    assert response.status_code == 200
    assert response.json()['enabled'] is False


def test_oidc_start_redirects_with_override_urls(monkeypatch):
    monkeypatch.setenv('OIDC_ENABLED', '1')
    monkeypatch.setenv('OIDC_AUTHORIZE_URL', 'http://keycloak.local/auth')
    monkeypatch.setenv('OIDC_TOKEN_URL', 'http://keycloak.local/token')
    client = TestClient(app)

    response = client.get('/auth/oidc/start', follow_redirects=False)

    assert response.status_code == 302
    location = response.headers['location']
    assert location.startswith('http://keycloak.local/auth?')
    params = parse_qs(urlparse(location).query)
    assert params['client_id'][0] == 'mvp-contact-center'
    assert params['response_type'][0] == 'code'
    assert 'state' in params


def test_oidc_callback_accepts_mapped_mock_user(monkeypatch):
    monkeypatch.setenv('OIDC_ENABLED', '1')
    monkeypatch.setenv('OIDC_TEST_MODE', '1')
    monkeypatch.setenv('OIDC_AUTHORIZE_URL', 'http://keycloak.local/auth')
    monkeypatch.setenv('OIDC_TOKEN_URL', 'http://keycloak.local/token')
    client = TestClient(app)

    start = client.get('/auth/oidc/start', follow_redirects=False)
    params = parse_qs(urlparse(start.headers['location']).query)
    state = params['state'][0]

    response = client.get(
        f'/auth/oidc/callback?state={state}&mock_sub=sub-1&mock_username=kc.user&mock_groups=kc_operator'
    )

    assert response.status_code == 200
    assert 'oidc-login' in response.text
    assert 'Corporate login completed' in response.text


def test_oidc_callback_rejects_unmapped_mock_user(monkeypatch):
    monkeypatch.setenv('OIDC_ENABLED', '1')
    monkeypatch.setenv('OIDC_TEST_MODE', '1')
    monkeypatch.setenv('OIDC_AUTHORIZE_URL', 'http://keycloak.local/auth')
    monkeypatch.setenv('OIDC_TOKEN_URL', 'http://keycloak.local/token')
    client = TestClient(app)

    start = client.get('/auth/oidc/start', follow_redirects=False)
    params = parse_qs(urlparse(start.headers['location']).query)
    state = params['state'][0]

    response = client.get(
        f'/auth/oidc/callback?state={state}&mock_sub=sub-2&mock_username=kc.bad&mock_groups=unknown_group'
    )

    assert response.status_code == 403
    assert 'OIDC role is not mapped' in response.text
