import axios from "axios";
import { loadAsync, makeRedirectUri, ResponseType } from "expo-auth-session";
import * as WebBrowser from 'expo-web-browser';
import { maybeCompleteAuthSession } from "expo-web-browser";
import jwt_decode from 'jwt-decode';
import React, { useState } from "react";
import { ActivityIndicator, Image, StyleSheet, Text, TouchableOpacity, View } from "react-native";
import { UnAuthStackNavigationProp } from "../../navigation/UnauthorizedNavigator";
import { authStore, IdToken } from "../../stores/AuthStore";
import { API_URL, reportAxiosError } from "../../utils/RequestUtils";
import { PrimaryButton, SecondaryButton } from "../Buttons";
import UnauthorizedLayout from "./AuthLayout";

/**
 * Props used by the {@link Intro} screen.
 */
export interface IntroProps {
    /**The {@link AuthStackNavigationProp} navigation object used to interact with the {@link Auth} navigator.*/
    navigation: UnAuthStackNavigationProp;
}

/**
 * A base url for the api's authorization endpoints
 */
const issuer = API_URL + "/o";

/**
 * An object containing the discovery endpoints for the api, necessary for OIDC authentication {@link https://swagger.io/docs/specification/authentication/openid-connect-discovery/}
 */
const discovery = {
    authorizationEndpoint: issuer + "/authorize/",
    tokenEndpoint: issuer + "/token/",
    revocationEndpoint: issuer + "/revoke/",
};

/**
 * Creates a browser session through which the user will login
 */
maybeCompleteAuthSession();

/**
 * The Intro screen displayed to unauthenticated users. Contains necessary methods to facilitate login and navigation to the [Registration]{@link RegisterMain} screens.
 * @component
 * @category Unauthorized
 */
const Intro : React.FC<IntroProps> = ({navigation}) => {
    /**
     * @type {string} 
     * React state holding the login result message to display to the user.
     * */
    const loginMessageState = "";
    const [loginMessage, setLoginMessage] = useState<string>(loginMessageState);
    /**
     * @type {boolean} 
     * React state holding the error state of the component.
     * */
     const errorState = false;
    const [error, setError] = useState<boolean>(errorState);

    const redirectUri = makeRedirectUri({
        path: 'callback'
    });

    /**
     * Function that initiates the login flow. It opens up with the in app browser and sends an authorization request to the API, which redirects the user to the backend login page. 
     * If the credentials entered are valid, the OAuth Authorization Code flow will occur, which results in the user recieving an access token and refresh token which are then stored in {@link AuthStore}.
     * */
    const login = async () => {
        console.log('User is attempting to login, opening web portal login...')

        // initiate authentication request to the server 
        const request = await loadAsync({
            clientId: "atlas.mobile",
            responseType: ResponseType.Code,
            redirectUri,
            usePKCE: true,
            scopes: ['openid'],
            
        }, discovery)  

        // handle authentication response from the server
        const response = await request.promptAsync(discovery);

        // if succesful, prepare a request for an access/id token
        if (response.type == "success" && request.codeVerifier) {
            console.log('User authentication was successful.')
            setLoginMessage("Logging you in...");
            const tokenData = new URLSearchParams();
            tokenData.append('grant_type', 'authorization_code');
            tokenData.append('client_id', 'atlas.mobile');
            tokenData.append('code', response.params.code);
            tokenData.append('redirect_uri', request.redirectUri);
            tokenData.append('code_verifier', request.codeVerifier);  
            tokenData.append('notif-token', authStore.notificationToken);
            console.log('Attempting to retrieve access token...')

            // send the token request
            try {
                const response = await axios.post(API_URL + `/o/token/`, tokenData, {
                    headers: {
                        'Content-Type': "application/x-www-form-urlencoded"
                    },
                });  

                const tokenResponse = response.data;

                // if its a successful response, decode the jwt id token, and store the tokens in the corresponding stores
                const idToken = jwt_decode(tokenResponse.id_token) as IdToken;

                await authStore.setAccessTokenAsync(tokenResponse.access_token);
                await authStore.setRefreshTokenAsync(tokenResponse.refresh_token);
                await authStore.setIdAsync(idToken.sub)

                console.log('Tokens successfully retrieved.')
            } catch (error) {
                reportAxiosError("Something went wrong when retrieving access token", error);
                setLoginMessage("Something went wrong when trying to log you in.")
                setError(true);
            } 
        }
    }   

    /**
     * Navigates to {@link RegisterMain}.
     */
    const goToRegistration = (): void => {
        console.log('Navigating to registration page.')
        navigation.navigate("Register");
    }

    /**
     * Opens up the [privacy policy page]{@link https://app.clicknpush.ca/privacy}.
     */
    const openPrivacyPolicy = async () => {
        console.log('Opening privacy policy.')
        await WebBrowser.openBrowserAsync(API_URL + "/privacy")
    }

    return (
        <UnauthorizedLayout>
            {!loginMessage ?
            <View style={styles.introContainer}>
                <View style={styles.brandContainer}>
                    <Image style={{flex: 1}} resizeMode="contain" source={require('../../../assets/logo-white.png')}></Image>
                    <Text style={styles.title} >Click & Push</Text>
                </View>
                <View style={styles.btnContainer}>
                    <PrimaryButton text="Login" onPress={login}/>
                    <SecondaryButton text="Create account" onPress={goToRegistration} />
                </View>
            </View> :
            <View style={{height: '100%', justifyContent: "center", alignItems: "center"}}>
                <Text style={{color: 'white', fontSize: 20, marginBottom: 30}}>{loginMessage}</Text>
                {!error ? <ActivityIndicator color='white' size="large"/> : <PrimaryButton text="Okay" onPress={() => {setError(false); setLoginMessage('')}}/> }
            </View> }
            <TouchableOpacity onPress={async () => await openPrivacyPolicy()}>
                <Text style={{fontSize: 12, textDecorationLine: "underline", alignSelf: "flex-end", color: 'white'}}>Privacy policy</Text>
            </TouchableOpacity>
        </UnauthorizedLayout>
    )
}

const styles = StyleSheet.create({
    introContainer: {
        flex: 1,
        marginVertical: 50,
        alignItems: "center",
        justifyContent: 'space-between'
    },
    brandContainer: {
        flex: 2,
        marginVertical: 50,
        alignItems: "center",
    },
    title: {
        marginTop: 30, 
        color: 'white',
        fontSize: 30
    },
    btnContainer: {
        flex: 1,
        alignItems: 'center',
        width: '100%',
    },
    registerBtn: {
        borderColor: 'white',
        borderWidth: 2,
        backgroundColor: 'transparent',
    }
})

export default Intro;