import{Tokens as t,clearTokensInStorage as e}from"./tokens.js";import{SEARCH_PARAM_AUTH_CODE as a,SEARCH_PARAM_ERROR as o,STORAGE_CODE_VERIFIER_NAME as r,BROADCAST_CHANNEL_NAME as n,STORAGE_POPUP_NAME as i,SEARCH_PARAM_ERROR_DESCRIPTION as s}from"./constants.js";import{iframeSettings as u}from"./iframe-url.js";import{S256 as h,newCodeVerifier as d}from"./code.js";import{resettablePromise as l}from"./reset-promise.js";import{TypedEventTarget as c}from"./TypedEventTarget.js";export const authState={initialized:!1,bootCalled:!1,forwardAuth:!0,redirectUri:"",authComplete:l(),clientId:"",oauthScope:"",broadcastChannel:null,get editorOrigin(){return u.editorOrigin},tokens:null,cleanup:[]};const p=new c,f=new c;function broadcastMessage(t){authState.broadcastChannel&&(authState.broadcastChannel.postMessage(t),"true"===localStorage.getItem(i)&&"auth-logout"!==t.type&&(localStorage.removeItem(i),setTimeout((()=>{window.close()}))))}export const auth={init({editorOrigin:i,clientId:h,scope:d,forwardAuth:l,redirectUri:c}){if(authState.initialized)throw new Error("Init should only be called once");if(authState.bootCalled)throw new Error("`auth.init` should always be called before `WebContainer.boot`");authState.initialized=!0,authState.forwardAuth=l??!0,authState.redirectUri=c??defaultRedirectUri();let w=!0;if(authState.tokens=t.fromStorage(),authState.clientId=h,authState.oauthScope=d,authState.broadcastChannel=new BroadcastChannel(n),u.setQueryParam("client_id",h),i&&(u.editorOrigin=new URL(i).origin),f.listen((()=>authState.authComplete.reset())),authState.broadcastChannel.addEventListener("message",(async function onChannelMessage(e){const a=e.data;if("auth-complete"===a.type)return authState.tokens=t.fromStorage(),authState.tokens,await authState.tokens.activate(w,onFailedTokenRefresh),void authState.authComplete.resolve();if("auth-failed"===a.type)return void p.fireEvent(a);if("auth-logout"===a.type)return void f.fireEvent()})),authState.tokens){const t=authState.tokens;if(t.origin===authState.editorOrigin)return(async()=>{if(await t.activate(w,onFailedTokenRefresh))authState.authComplete.resolve();else{if(authState.tokens!==t)return;f.fireEvent()}})(),{status:"authorized"};e(),authState.tokens=null}const g=new URL(window.location.href),{searchParams:S}=g,updateURL=()=>window.history.replaceState({},document.title,g);if(S.has(o)){const t=S.get(o),e=S.get(s);return S.delete(o),S.delete(s),updateURL(),broadcastMessage({type:"auth-failed",error:t,description:e}),{status:"auth-failed",error:t,description:e}}if(S.has(a)){const e=S.get(a),o=authState.editorOrigin;S.delete(a),updateURL();const n=localStorage.getItem(r);if(!n)return{status:"need-auth"};localStorage.removeItem(r);let i=defaultRedirectUri();return t.fromAuthCode({editorOrigin:o,clientId:authState.clientId,authCode:e,codeVerifier:n,redirectUri:i}).then((async t=>{authState.tokens=t,assertAuthTokens(authState.tokens);if(!await authState.tokens.activate(w,onFailedTokenRefresh))throw new Error;authState.authComplete.resolve(),broadcastMessage({type:"auth-complete"})})).catch((t=>{console.error(t),f.fireEvent(),broadcastMessage({type:"auth-logout"})})),{status:"authorized"}}return{status:"need-auth"}},async startAuthFlow({popup:t}={}){if(!authState.initialized)throw new Error("auth.init must be called first");if(t){localStorage.setItem(i,"true");const t=500,e=620,a=window.screenLeft+(window.outerWidth-e)/2,o=window.screenTop+(window.outerHeight-t)/2;window.open(await generateOAuthRequest(),"_blank",`popup,width=${e},height=${t},left=${a},top=${o}`)}else window.location.href=await generateOAuthRequest()},async logout({ignoreRevokeError:t}={}){await(authState.tokens?.revoke(authState.clientId,t??!1)),f.fireEvent(),broadcastMessage({type:"auth-logout"})},loggedIn:()=>authState.authComplete.promise,on(t,e){switch(t){case"auth-failed":return p.listen(e);case"logged-out":return f.listen(e);default:throw new Error(`Unsupported event type '${t}'.`)}}};function onFailedTokenRefresh(){f.fireEvent(),broadcastMessage({type:"auth-logout"})}function defaultRedirectUri(){return window.location.href}async function generateOAuthRequest(){const t=d();localStorage.setItem(r,t);const e=await h(t),a=new URL("/oauth/authorize",authState.editorOrigin),{searchParams:o}=a;let n=defaultRedirectUri();return o.append("response_type","code"),o.append("client_id",authState.clientId),o.append("redirect_uri",n),o.append("scope",authState.oauthScope),o.append("code_challenge",e),o.append("code_challenge_method","S256"),a.toString()}export function assertAuthTokens(t){if(!t)throw new Error("Oops! Tokens is not defined when it always should be.")}