import { Injectable } from "@angular/core";
import { RPCSocket } from 'rpclibrary/js/src/Frontend'
import { ConnectedSocket } from "rpclibrary"
import { Auth, User, _Class, FrontcraftFeatureIfc, SomeOf, FrontcraftIfc, } from '../../../../../../backend/Types/Types'
import { hash, IApiService, GuestUser } from './ApiService';
import { LoggerService } from '../LoggerService/logger.service';

declare const Cookies

@Injectable()
export class ClientApiService implements IApiService{
    private socket:ConnectedSocket<FrontcraftIfc>
    private auth:Auth
    private privSocket: ConnectedSocket<SomeOf<FrontcraftFeatureIfc>>

    constructor(
        private logger: LoggerService
    ){
        window['s'] = this
    }

    getUnprivilegedSocket = () : ConnectedSocket<FrontcraftIfc> => this.socket

    private getPrivilegedSocket = async (auth:Auth) : Promise<ConnectedSocket<SomeOf<FrontcraftFeatureIfc>>> => {
        if(this.privSocket) {
            return this.privSocket
        }
        if(auth == null) 
            throw new Error("Bad Auth")

        try{
            const sock = new RPCSocket(
                auth.port, 
                window.location.hostname,
            )

            sock.hook('kick', () => {
                this.kick()
            })

            sock.hook('getUserData', () => auth)

            sock.on('close', async () => {
                this.logger.log("priv#close");
                
                //handled via unprivileged socket
            })

            sock.on('error', async (e) => {
                this.logger.log("priv#error", e);
                await this.logout()
                //handled via unprivileged socket
            })
            const authSock = await sock.connect(auth.token.value)
            
            this.auth = auth
            this.privSocket = authSock
            Cookies.set('token', JSON.stringify(auth))
//            document.cookie = JSON.stringify(auth)
            return authSock
        }catch(e){ 
            if(this.privSocket) this.privSocket.close()
            return;
        }
    }

    get = <K extends (keyof FrontcraftIfc | keyof FrontcraftFeatureIfc)>(feature : K) : K extends keyof FrontcraftIfc?FrontcraftIfc[K]:
                                                                                        K extends keyof FrontcraftFeatureIfc?(FrontcraftFeatureIfc[K] | void): 
                                                                                        never => {
        try{
            //@ts-ignore
            if(this.socket && this.socket[feature]) return this.socket[feature]
            //@ts-ignore
            if(this.privSocket && this.privSocket[feature]) return this.privSocket[feature]
        }catch(e){
            return
        } 
    }

    getCurrentUser = () : User => {
        return this.auth?this.auth.user:GuestUser
    }

    login = async (username: string, password: string) : Promise<RPCSocket & SomeOf<FrontcraftFeatureIfc>> => {
        const pwHash = await hash(password)
        let auth
        try{
            auth = await this.socket.UserManager.login(username, pwHash)
        }catch(e){
            return
        }

        if(!auth){ 
            await this.logout()
            throw new Error("Login failed")
        }

        this.socket.on('error', async (err) => {
            location.reload()
        })

        const sock = await this.getPrivilegedSocket(auth)
        return sock
    }

    kick = async () => {
        await this.logout()
        location.reload()
    }

    logout = async () => {
        Cookies.remove('token')
        if(this.privSocket) this.privSocket.unhook('kick')
        if(this.auth){
            try{
                await this.socket.UserManager.logout(this.auth.user.username, this.auth.token.value)
            }catch(e){
                console.warn(e);
            }
        } 

        if(this.privSocket) this.privSocket.close()
        this.privSocket = null
        this.auth  = null
    }

    initialize = async (force = false) : Promise<any> => {
        if(this.socket){
            this.socket.close()
            this.socket = null
        }

        try{
            let conn = new RPCSocket<FrontcraftIfc>(8080, window.location.hostname)
            conn.on('close', async () => {
            })                        

            this.socket = await conn.connect()
        }catch(e){
            alert('Unable to connect. The server appears to be down.\nPress OK to refresh the page')
            location.reload()
            return;
        }

        try{
            const cookie = JSON.parse(Cookies.get('token'))
            
            if(cookie != null && cookie != "") {
                try{
                    const auth = await this.socket.UserManager.getAuth(cookie.token.value)
                    if(!auth) return this.socket
                    return await this.getPrivilegedSocket(auth)
                }catch(e){
                    await this.logout()
                    return
                }
            }
        }catch(e){
            Cookies.remove('token')
        }
    }

    getAuth = () => this.auth

    async checkLogin() : Promise<boolean>{
        if(!this.auth) return false
        const valid = this.socket.UserManager.checkToken(this.auth.token.value, this.auth.user.rank)
        if(valid) return true
        await this.logout()
        return false
    }
}