import { render } from 'solid-js/web';
import { For, Switch, Match, createSignal, createEffect, onMount } from 'solid-js';
import { Router, Route, useSearchParams, useParams } from '@solidjs/router';
import * as microsoftTeams from "@microsoft/teams-js";
import api from '@kantree/api';
import { asyncComponent } from '@kantree/utils/solid';

import '@public/less/bootstrap-icons.css';
import 'loaders.css/loaders.css';

const KANTREE_SESSION_KEY = 'kantree-msteams-token';

const TRANSLATIONS = {
  "fr": {
    "You are now connected, this window will close shortly.": "Vous êtes maintenant connecté, cette fenêtre va se fermer automatiquement.",
    "Connect to your Kantree account to continue": "Connectez-vous à votre compte Kantree pour continuer",
    "Connect": "Connexion",
    "An error occured": "Une erreur est survenue",
    "Shared": "Partagé",
    "Personal": "Personel",
    "Select a workspace": "Sélectionnez un workspace",
    "You haven't joined any workspaces in your Kantree account.": "Vous n'avez pas rejoint de Workspace dans votre compte Kantree.",
    "What do you wish to add to your team:": "Que souhaitez-vous ajouter à votre équipe:",
    "Add the full workspace (a Kantree account with rights to access the workspace will be needed)": "Ajouter le workspace entier (un compte Kantree avec les droits adéquats sera nécessaire)",
    "Add a single view using a shared link (read only without Kantree account)": "Ajouter une seule vue via un lien de partage (lecture seule sans compte Kantree)",
    "No category": "Pas de catégorie",
    "You are logged in to Kantree for Microsoft Teams as": "Vous êtes connecté à Kantree pour Microsoft Teams en tant que",
    "Logout": "Se déconnecter"
  }
};

function detectLocale() {
  return (new URL(document.location)).searchParams.get('locale') || (window.navigator?.language || '').substring(0, 2) || 'en';
}

const [currentUser, setCurrentUser] = createSignal(null);
const [currentLocale, setCurrentLocale] = createSignal(detectLocale());

function _(text) {
  if (currentLocale() === 'en') {
    return text;
  }
  if (TRANSLATIONS?.[currentLocale()]?.[text]) {
    return TRANSLATIONS[currentLocale()][text];
  }
  return text;
}

function externalUrl(path) {
  return `https://${document.location.hostname}${path}`;
}

function routeUrl(path, external=false) {
  const url = `/integrations/msteams/tab${path}`;
  return external ? externalUrl(url) : url;
}

function Loader() {
  return <div class="loader"><div class="loader-inner ball-clip-rotate"><div></div></div></div>;
}

function ShowLoader(props) {
  return <Switch fallback={<Loader />}><Match when={props.when}>{props.children}</Match></Switch>;
}

function AuthEnd() {
  const [params] = useSearchParams();
  microsoftTeams.authentication.notifySuccess(params.access_token, '');
  return <div>{_("You are now connected, this window will close shortly.")}</div>;
}

function LoginScreen(props) {
  async function handleLoginClick() {
    const url = routeUrl(`/auth?oauthRedirectMethod={oauthRedirectMethod}&authId={authId}`, true);
    const accessToken = await microsoftTeams.authentication.authenticate({url, isExternal: false });
    if (accessToken) {
      localStorage.setItem(KANTREE_SESSION_KEY, accessToken);
    }
    props.success();
  }

  return <div id="login-screen">
    <img src="/static/assets/logo-black.png" />
    <h1>{_("Connect to your Kantree account to continue")}</h1>
    <button onClick={handleLoginClick}>{_("Connect")}</button>
  </div>;
};

const RequireAuth = (props) => {
  const fetchUserInfo = async () => {
    try {
      const accessToken = localStorage.getItem(KANTREE_SESSION_KEY);
      if (accessToken) {
        api.setAuthToken(accessToken);
        window.kantreeAccessToken = accessToken;
        const userInfo = await api.user.get_info.get();
        setCurrentUser(userInfo);
        setCurrentLocale(userInfo.locale)
      } else {
        setCurrentUser(false);
        setCurrentLocale(detectLocale());
      }
    } catch(e) {
      setCurrentUser(false);
      setCurrentLocale(detectLocale());
    }
  };

  onMount(() => fetchUserInfo());

  return (
    <Switch fallback={<Loader />}>
      <Match when={currentUser()}>
        {props.children}
      </Match>
      <Match when={currentUser() === false}>
        <LoginScreen success={fetchUserInfo}></LoginScreen>
      </Match>
    </Switch>
  );
};

function TabConfig() {
  const [loaded, setLoaded] = createSignal(false);
  const [workspaceOptions, setWorkspaceOptions] = createSignal([]);
  const [selectedWorkspace, setSelectedWorkspace] = createSignal();
  const [addSelection, setAddSelection] = createSignal('workspace');
  const [viewOptions, setViewOptions] = createSignal([]);
  const [selectedView, setSelectedView] = createSignal();

  let workspacesById = {};
  let viewsById = {};

  onMount(async () => {
    microsoftTeams.pages.config.registerOnSaveHandler(async (saveEvent) => {
      try {
        let websiteUrl, contentUrl, entityId, suggestedDisplayName;
        if (addSelection() === 'view') {
          const sharing = await api.share_objects.create.post({
            project_id: selectedWorkspace().id,
            object_type: 'view',
            object_id: selectedView().id
          });
          websiteUrl = externalUrl(`/share/${sharing.token}`);
          contentUrl = routeUrl(`/view/${sharing.token}`, true);
          entityId = `kantree-tab-${selectedWorkspace().id}-${sharing.token}`;
          suggestedDisplayName = `Kantree - ${selectedView().name}`;
        } else {
          websiteUrl = externalUrl(`/p/${selectedWorkspace().slug}`);
          contentUrl = routeUrl(`/ws/${selectedWorkspace().slug}`, true);
          entityId = `kantree-tab-${selectedWorkspace().id}`;
          suggestedDisplayName = `Kantree - ${selectedWorkspace().title}`;
        }
        await microsoftTeams.pages.config.setConfig({
          websiteUrl,
          contentUrl,
          entityId,
          suggestedDisplayName
        });
        saveEvent.notifySuccess();
      } catch (e) {
        saveEvent.notifyFailure(_("An error occured"));
      }
    });

    const r = await api.user.list_app_context_content.get();
    const options = [];
    for (const [key, label] of [['shared', _('Shared')], ['user', _('Personal')]]) {
      if (r[key] && r[key].length) {
        options.push({
          label,
          projects: r[key]
        });
      }
    }
    for (const [org, projects, teams] of r.orgs) {
      const noTeamProjects = projects.filter(p => !p.team);
      if (noTeamProjects.length) {
        options.push({
          label: org.name,
          projects: noTeamProjects
        });
      }
      for (const team of teams) {
        const teamProjects = projects.filter(p => p.team?.id === team.id);
        if (teamProjects.length) {
          options.push({
            label: `${org.name} / ${team.name}`,
            projects: teamProjects
          });
        }
      }
    }

    workspacesById = {};
    setSelectedWorkspace(null);
    for (const group of options) {
      group.projects.sort((a, b) => a.title.localeCompare(b.title));
      for (const project of group.projects) {
        workspacesById[project.id] = project;
        if (!selectedWorkspace()) {
          setSelectedWorkspace(project);
        }
      }
    }

    setWorkspaceOptions(options);
    setAddSelection('workspace');
    setLoaded(true);
  });

  createEffect(async () => {
    if (!selectedWorkspace()) {
      return;
    }

    const r = await api.projects.get_board.get({project_id: selectedWorkspace().id});
    const categories = {0: {label: _("No category"), views: []}};
    for (const category of r.view_categories) {
      categories[category.id] = {label: category.name, views: []};
    }
    for (const view of r.views) {
      categories[view.category_id || 0].views.push(view);
    }

    viewsById = {};
    setSelectedView(null);
    const options = Object.values(categories).map(c => {
      c.views.sort((a, b) => a.name.localeCompare(b.name));
      for (const view of c.views) {
        viewsById[view.id] = view;
        if (!selectedView()) {
          setSelectedView(view);
        }
      }
      return c;
    })

    options.sort((a, b) => a.label.localeCompare(b.label));
    setViewOptions(options);
  });

  createEffect(() => {
    microsoftTeams.pages.config.setValidityState(!!selectedWorkspace() && (addSelection() !== 'view' || !!selectedView()));
  });

  return (
    <ShowLoader when={loaded()}>
      <div id="tab-config-screen">
        <Show when={workspaceOptions().length > 0}>
          <h1>{_("Select a workspace")}</h1>
          <select value={selectedWorkspace()?.id} onInput={e => setSelectedWorkspace(workspacesById[parseInt(e.target.value)])}>
            <For each={workspaceOptions()}>{(group) =>
              <optgroup label={group.label}>
                <For each={group.projects}>{(ws) =>
                  <option value={ws.id}>{ws.title}</option>
                }</For>
              </optgroup>
            }</For>
          </select>
          <h1>{_("What do you wish to add to your team:")}</h1>
          <p>
            <label>
              <input type="radio" name="addselection" value="workspace" checked={addSelection() === 'workspace'} onChange={(e) => setAddSelection(e.target.value)} />
              {_("Add the full workspace (a Kantree account with rights to access the workspace will be needed)")}
            </label>
          </p>
          <p>
            <label>
              <input type="radio" name="addselection" value="view" checked={addSelection() === 'view'} onChange={(e) => setAddSelection(e.target.value)} />
              {_("Add a single view using a shared link (read only but no Kantree account will be needed)")}
            </label>
          </p>
          <Show when={addSelection() === 'view'}>
            <ShowLoader when={viewOptions().length}>
              <select value={selectedView()?.id} onInput={e => setSelectedView(viewsById[parseInt(e.target.value)])}>
                <For each={viewOptions()}>{(group) =>
                  <optgroup label={group.label}>
                    <For each={group.views}>{(view) =>
                      <option value={view.id}>{view.name}</option>
                    }</For>
                  </optgroup>
                }</For>
              </select>
            </ShowLoader>
          </Show>
        </Show>
        <Show when={workspaceOptions().length === 0}>
          <div>{_("You haven't joined any workspaces in your Kantree account.")}</div>
        </Show>
      </div>
    </ShowLoader>
  );
}

function KantreeContainer(props) {
  const handleLogoutClick = () => {
    localStorage.removeItem(KANTREE_SESSION_KEY);
    setCurrentUser(false);
  };

  return (
    <div id="kantree-app-frame">
      <iframe src={props.url}></iframe>
      <Show when={currentUser()}>
        <div>
          <span>{_("You are logged in to Kantree for Microsoft Teams as")} <em>{currentUser().display_name}</em></span>
          <button onClick={handleLogoutClick}>{_("Logout")}</button>
        </div>
      </Show>
    </div>
  );
};

function WorkspaceContainer() {
  const params = useParams();
  return <RequireAuth><KantreeContainer url={externalUrl(`/p/${params.slug}`)} /></RequireAuth>;
}

function ViewContainer() {
  const params = useParams();
  return <KantreeContainer url={externalUrl(`/share/${params.token}`)} />;
}

function InboxContainer() {
  return <RequireAuth><KantreeContainer url={externalUrl('/inbox')} /></RequireAuth>;
}

const App = asyncComponent(async () => {
  await microsoftTeams.app.initialize();
  return () => (
    <Router>
      <Route path={routeUrl('/config')} component={() => <RequireAuth><TabConfig/></RequireAuth>} />
      <Route path={routeUrl('/auth-end')} component={AuthEnd} />
      <Route path={routeUrl('/ws/:slug')} component={WorkspaceContainer} />
      <Route path={routeUrl('/view/:token')} component={ViewContainer} />
      <Route path={routeUrl('/personal')} component={InboxContainer} />
    </Router>
  );
});

render(App, document.getElementById('app'));
