/**
 * External Auth: workflow for logging in to remote servers.
 * @module pl-fe/actions/external_auth
 * @see module:pl-fe/actions/auth
 * @see module:pl-fe/actions/apps
 * @see module:pl-fe/actions/oauth
 */

import { instanceSchema, PlApiClient, type Instance } from 'pl-api';
import * as v from 'valibot';

import { createApp } from '@/actions/apps';
import { authLoggedIn, verifyCredentials, switchAccount } from '@/actions/auth';
import { obtainOAuthToken } from '@/actions/oauth';
import { parseBaseURL } from '@/utils/auth';
import sourceCode from '@/utils/code';
import { getInstanceScopes } from '@/utils/scopes';

import type { AppDispatch } from '@/store';

const fetchExternalInstance = (baseURL: string) =>
  new PlApiClient(baseURL).instance
    .getInstance()
    .then((instance) => instance)
    .catch((error) => {
      if (error.response?.status === 401) {
        // Authenticated fetch is enabled.
        // Continue with a limited featureset.
        return v.parse(instanceSchema, {});
      } else {
        throw error;
      }
    });

const createExternalApp = (instance: Instance, baseURL?: string) => {
  const params = {
    client_name: `${sourceCode.displayName} (${new URL(window.origin).host})`,
    redirect_uris: `${window.location.origin}/login/external`,
    website: sourceCode.homepage,
    scopes: getInstanceScopes(instance, undefined, true),
  };

  return createApp(params, baseURL);
};

const externalAuthorize = (instance: Instance, baseURL: string) => {
  const scopes = getInstanceScopes(instance, undefined, true);

  return createExternalApp(instance, baseURL).then((app) => {
    const { client_id, redirect_uri } = app;

    const query = new URLSearchParams({
      client_id,
      redirect_uri: redirect_uri ?? app.redirect_uris[0],
      response_type: 'code',
      scope: scopes,
    });

    localStorage.setItem('plfe:external:app', JSON.stringify(app));
    localStorage.setItem('plfe:external:baseurl', baseURL);
    localStorage.setItem('plfe:external:scopes', scopes);

    window.location.href = `${baseURL}/oauth/authorize?${query.toString()}`;
  });
};

const externalLogin = (host: string) => {
  const baseURL = parseBaseURL(host) || parseBaseURL(`https://${host}`);

  return fetchExternalInstance(baseURL).then((instance) => {
    externalAuthorize(instance, baseURL);
  });
};

const loginWithCode = (code: string) => (dispatch: AppDispatch) => {
  const app = JSON.parse(localStorage.getItem('plfe:external:app')!);
  const { client_id, client_secret, redirect_uri } = app;
  const baseURL = localStorage.getItem('plfe:external:baseurl')!;
  const scope = localStorage.getItem('plfe:external:scopes')!;

  const params = {
    client_id,
    client_secret,
    redirect_uri,
    grant_type: 'authorization_code',
    scope,
    code,
  };

  return obtainOAuthToken(params, baseURL)
    .then((token) => dispatch(authLoggedIn(token, app)))
    .then(({ access_token }) => dispatch(verifyCredentials(access_token, baseURL)))
    .then((account) => dispatch(switchAccount(account.id)))
    .then(() => (window.location.href = '/'));
};

export { externalLogin, loginWithCode };
