import type { LoginState } from "@hibas123/openauth-internalapi";
import { derived, get, writable } from "svelte/store";
import InternalAPI from "../../helper/api";
import sha from "../../helper/sha512";

interface LocalLoginState extends LoginState {
   loading: boolean;
   error?: string;
   username?: string;
}

class LoginStore {
   state = writable<LocalLoginState>({
      username: undefined,
      password: false,
      passwordSalt: undefined,
      requireTwoFactor: [],
      success: false,
      loading: true,
      error: undefined
   })

   isFinished = derived(this.state, $state => $state.success);

   constructor() {
      this.state.subscribe((state) => {
         if (state.success) {
            setTimeout(() => {
               this.finish();
            }, 2000);
         }
      })
      this.getState();
   }

   setLoading(loading: boolean) {
      this.state.update(current => ({
         ...current,
         loading,
         error: loading ? undefined : current.error,
      }));
   }

   setError(error: string) {
      this.state.update(current => ({
         ...current,
         error,
      }));
   }


   async getState() {
      try {
         this.setLoading(true);

         let state = await InternalAPI.Login.GetState();
         this.state.update(current => ({
            ...current,
            ...state,
         }));
      } catch (err) {
         this.setError(err.message);
      } finally {
         this.setLoading(false);
      }
   }

   async setUsername(username: string) {
      try {
         this.setLoading(true);

         let state = await InternalAPI.Login.Start(username);
         this.state.update(current => ({
            ...current,
            ...state,
            username
         }));
      } catch (err) {
         this.setError(err.message);
      } finally {
         this.setLoading(false);
      }

   }

   async setPassword(password: string) {
      try {
         this.setLoading(true);

         const date = new Date().valueOf();
         let salt = get(this.state).passwordSalt
         let pw = sha(sha(salt + password) + date.toString());

         let state = await InternalAPI.Login.UsePassword(pw, date);
         this.state.update(current => ({
            ...current,
            ...state,
         }));
      } catch (err) {
         this.setError(err.message);
      } finally {
         this.setLoading(false);
      }
   }

   async useTOTP(id: string, code: string) {
      try {
         this.setLoading(true);

         let state = await InternalAPI.Login.UseTOTP(id, code);
         this.state.update(current => ({
            ...current,
            ...state,
         }));
      } catch (err) {
         this.setError(err.message);
      } finally {
         this.setLoading(false);
      }
   }

   async useBackupCode(id: string, code: string) {
      try {
         this.setLoading(true);

         let state = await InternalAPI.Login.UseBackupCode(id, code);
         this.state.update(current => ({
            ...current,
            ...state,
         }));
      } catch (err) {
         this.setError(err.message);
      } finally {
         this.setLoading(false);
      }
   }

   async getWebAuthnChallenge(id: string) {
      try {
         this.setLoading(true);

         let challenge = await InternalAPI.Login.GetWebAuthnChallenge(id);
         return challenge;
      } catch (err) {
         this.setError(err.message);
      } finally {
         this.setLoading(false);
      }
   }

   async useWebAuthn(id: string, response: any) {
      try {
         this.setLoading(true);

         let state = await InternalAPI.Login.UseWebAuthn(id, JSON.stringify(response));
         this.state.update(current => ({
            ...current,
            ...state,
         }));
      } catch (err) {
         this.setError(err.message);
      } finally {
         this.setLoading(false);
      }
   }

   async finish() {
      let url = new URL(window.location.href);
      let state = url.searchParams.get("state");
      let red = "/";

      if (state) {
         let base64 = url.searchParams.get("base64");
         if (base64) red = atob(state);
         else red = state;
      }
      setTimeout(() => (window.location.href = red), 200);
   }
}

const loginState = new LoginStore();

export default loginState;
