"""
Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved.

This software is licensed under the Apache License, Version 2.0 (the
"License") as published by the Apache Software Foundation.

You may not use this file except in compliance with the License. You may
obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations
under the License.
"""
import asyncio
import json

from fastapi import FastAPI
from fastapi.requests import Request
from fastapi.testclient import TestClient
from pytest import fixture
from pytest import mark

from supertokens_python import init
from supertokens_python.recipe import session, emailpassword
from supertokens_python.framework.fastapi import Middleware
from supertokens_python.recipe.session import create_new_session, refresh_session, get_session
from tests.utils import (
    reset, setup_st, clean_st, start_st, sign_up_request
)


def setup_function(f):
    reset()
    clean_st()
    setup_st()


def teardown_function(f):
    reset()
    clean_st()


@fixture(scope='function')
async def driver_config_client():
    app = FastAPI()
    app.add_middleware(Middleware)

    @app.get('/login')
    async def login(request: Request):
        user_id = 'userId'
        await create_new_session(request, user_id, {}, {})
        return {'userId': user_id}

    @app.post('/refresh')
    async def custom_refresh(request: Request):
        await refresh_session(request)
        return {}

    @app.get('/info')
    async def info_get(request: Request):
        await get_session(request, True)
        return {}

    @app.get('/custom/info')
    def custom_info(_):
        return {}

    @app.options('/custom/handle')
    def custom_handle_options(_):
        return {'method': 'option'}

    @app.get('/handle')
    async def handle_get(request: Request):
        session = await get_session(request, True)
        return {'s': session.get_handle()}

    @app.post('/logout')
    async def custom_logout(request: Request):
        session = await get_session(request, True)
        await session.revoke_session()
        return {}

    return TestClient(app)


@mark.asyncio
async def test_email_validation_checks_in_generate_token_API(driver_config_client: TestClient):
    init({
        'supertokens': {
            'connection_uri': "http://localhost:3567",
        },
        'framework': 'fastapi',
        'app_info': {
            'app_name': "SuperTokens Demo",
            'api_domain': "http://api.supertokens.io",
            'website_domain': "supertokens.io",
            'api_base_path': "/auth"
        },
        'recipe_list': [
            emailpassword.init({})
        ],
    })
    start_st()

    response_1 = driver_config_client.post(
        url="/auth/user/password/reset/token",
        json={
            'formFields':
                [{
                    "id": "email",
                    "value": "random"
                }]
        })

    assert response_1.status_code == 200
    dict_response = json.loads(response_1.text)
    assert dict_response["status"] == "FIELD_ERROR"


@mark.asyncio
async def test_that_generated_password_link_is_correct(driver_config_client: TestClient):
    reset_url = None
    token_info = None
    rid_info = None

    async def custom_f(user, password_reset_url_with_token):
        nonlocal reset_url, token_info, rid_info
        reset_url = password_reset_url_with_token.split("?")[0]
        token_info = password_reset_url_with_token.split("?")[1].split("&")[0]
        rid_info = password_reset_url_with_token.split("?")[1].split("&")[1]

    init({
        'supertokens': {
            'connection_uri': "http://localhost:3567",
        },
        'framework': 'fastapi',
        'app_info': {
            'app_name': "SuperTokens Demo",
            'api_domain': "http://api.supertokens.io",
            'website_domain': "supertokens.io",
            'api_base_path': "/auth"
        },
        'recipe_list': [
            emailpassword.init({
                'reset_password_using_token_feature': {
                    'create_and_send_custom_email': custom_f
                }
            }),
            session.init()
        ],
    })
    start_st()

    response_1 = sign_up_request(
        driver_config_client,
        "test@gmail.com",
        "testPass123")
    assert response_1.status_code == 200
    dict_response = json.loads(response_1.text)
    assert dict_response["status"] == "OK"
    await asyncio.sleep(1)
    response_1 = driver_config_client.post(
        url="/auth/user/password/reset/token",
        json={
            'formFields':
                [{
                    "id": "email",
                    "value": "test@gmail.com"
                }]
        })
    await asyncio.sleep(1)

    assert response_1.status_code == 200
    assert reset_url == "https://supertokens.io/auth/reset-password"
    assert "token=" in token_info
    assert "rid=emailpassword" in rid_info


@mark.asyncio
async def test_password_validation(driver_config_client: TestClient):
    init({
        'supertokens': {
            'connection_uri': "http://localhost:3567",
        },
        'framework': 'fastapi',
        'app_info': {
            'app_name': "SuperTokens Demo",
            'api_domain': "http://api.supertokens.io",
            'website_domain': "supertokens.io",
            'api_base_path': "/auth"
        },
        'recipe_list': [
            emailpassword.init({})
        ],
    })
    start_st()

    response_1 = driver_config_client.post(
        url="/auth/user/password/reset",
        json={
            'formFields':
                [{
                    "id": "password",
                    "value": "invalid"
                }],
                "token": "random"
        })

    assert response_1.status_code == 200
    # dict_response = json.loads(response_1.text)
    # assert dict_response["status"] == "FIELD_ERROR"

    response_2 = driver_config_client.post(
        url="/auth/user/password/reset",
        json={
            'formFields':
                [{
                    "id": "password",
                    "value": "validpass123"
                }],
            "token": "randomToken"

        })

    assert response_2.status_code == 200
    dict_response = json.loads(response_2.text)
    assert dict_response["status"] != "FIELD_ERROR"


@mark.asyncio
async def test_token_missing_from_input(driver_config_client: TestClient):
    init({
        'supertokens': {
            'connection_uri': "http://localhost:3567",
        },
        'framework': 'fastapi',
        'app_info': {
            'app_name': "SuperTokens Demo",
            'api_domain': "http://api.supertokens.io",
            'website_domain': "supertokens.io",
        },
        'recipe_list': [
            emailpassword.init({})
        ],
    })
    start_st()

    response_1 = driver_config_client.post(
        url="/auth/user/password/reset",
        json={
            'formFields':
                [{
                    "id": "password",
                    "value": "validpass123"
                }]
        })

    assert response_1.status_code == 400
    dict_response = json.loads(response_1.text)
    assert dict_response["message"] == "Please provide the password reset token"


@mark.asyncio
async def test_valid_token_input_and_passoword_has_changed(driver_config_client: TestClient):
    token_info = None

    async def custom_f(user, password_reset_url_with_token):
        nonlocal token_info
        token_info = password_reset_url_with_token.split("?")[1].split("&")[
            0].split("=")[1]

    init({
        'supertokens': {
            'connection_uri': "http://localhost:3567",
        },
        'framework': 'fastapi',
        'app_info': {
            'app_name': "SuperTokens Demo",
            'api_domain': "http://api.supertokens.io",
            'website_domain': "supertokens.io",
            'api_base_path': "/auth"
        },
        'recipe_list': [
            emailpassword.init({
                'reset_password_using_token_feature': {
                    'create_and_send_custom_email': custom_f
                }
            }),
            session.init()
        ],
    })
    start_st()

    response_1 = sign_up_request(
        driver_config_client,
        "random@gmail.com",
        "validpass123")
    assert response_1.status_code == 200
    dict_response = json.loads(response_1.text)
    user_info = dict_response['user']
    assert dict_response["status"] == "OK"
    response_1 = driver_config_client.post(
        url="/auth/user/password/reset/token",
        json={
            'formFields':
                [{
                    "id": "email",
                    "value": "random@gmail.com"
                }]
        })
    await asyncio.sleep(1)

    assert response_1.status_code == 200

    response_2 = driver_config_client.post(
        url="/auth/user/password/reset",
        json={
            'formFields':
                [{
                    "id": "password",
                    "value": "validpass12345"
                }],
            "token": token_info
        })
    await asyncio.sleep(1)
    assert response_2.status_code == 200

    response_3 = driver_config_client.post(
        url="/auth/signin",
        json={
            'formFields':
                [{
                    "id": "password",
                    "value": "validpass123"
                },
                    {
                        "id": "email",
                        "value": "random@gmail.com"
                }
                ]
        })

    assert response_3.status_code == 200

    dict_response = json.loads(response_3.text)
    assert dict_response['status'] == 'WRONG_CREDENTIALS_ERROR'

    response_4 = driver_config_client.post(
        url="/auth/signin",
        json={
            'formFields':
                [
                    {
                        "id": "password",
                        "value": "validpass12345",
                    },
                    {
                        "id": "email",
                        "value": "random@gmail.com",
                    },
                ]
        })

    assert response_4.status_code == 200

    dict_response = json.loads(response_4.text)
    assert dict_response['status'] == 'OK'
    assert dict_response['user']['id'] == user_info['id']
    assert dict_response['user']['email'] == user_info['email']
