<script>
  import { writable, get as svelteGet } from "svelte/store"
  import { notifications, Heading, Button } from "@budibase/bbui"
  import { store, automationStore, hostingStore } from "builderStore"
  import { string, object } from "yup"
  import api, { get } from "builderStore/api"
  import Spinner from "components/common/Spinner.svelte"
  import { Info, User } from "./Steps"
  import Indicator from "./Indicator.svelte"
  import { goto } from "@roxi/routify"
  import { fade } from "svelte/transition"
  import { post } from "builderStore/api"
  import analytics from "analytics"
  import { onMount } from "svelte"
  import Logo from "/assets/bb-logo.svg"
  import { capitalise } from "../../helpers"

  export let template

  const currentStep = writable(0)
  const values = writable({ roleId: "ADMIN" })
  const errors = writable({})
  const touched = writable({})
  const steps = [Info, User]
  let validators = [
    {
      applicationName: string().required("Your application must have a name"),
    },
    {
      roleId: string()
        .nullable()
        .required("You need to select a role for this app"),
    },
  ]

  let submitting = false
  let valid = false
  $: checkValidity($values, validators[$currentStep])

  onMount(async () => {
    const hostingInfo = await hostingStore.actions.fetch()
    if (hostingInfo.type === "self") {
      await hostingStore.actions.fetchDeployedApps()
      const existingAppNames = svelteGet(hostingStore).deployedAppNames
      validators[0].applicationName = string()
        .required("Your application must have a name.")
        .test(
          "non-existing-app-name",
          "App with same name already exists. Please try another app name.",
          (value) =>
            !existingAppNames.some(
              (appName) => appName.toLowerCase() === value.toLowerCase()
            )
        )
    }
  })

  const checkValidity = async (values, validator) => {
    const obj = object().shape(validator)
    Object.keys(validator).forEach((key) => ($errors[key] = null))
    try {
      await obj.validate(values, { abortEarly: false })
    } catch (validationErrors) {
      validationErrors.inner.forEach((error) => {
        $errors[error.path] = capitalise(error.message)
      })
    }
    valid = await obj.isValid(values)
  }

  async function createNewApp() {
    submitting = true
    try {
      // Create form data to create app
      let data = new FormData()
      data.append("name", $values.applicationName)
      data.append("useTemplate", template != null)
      if (template) {
        data.append("templateName", template.name)
        data.append("templateKey", template.key)
        data.append("templateFile", template.file)
      }

      // Create App
      const appResp = await post("/api/applications", data, {})
      const appJson = await appResp.json()
      if (!appResp.ok) {
        throw new Error(appJson.message)
      }

      analytics.captureEvent("App Created", {
        name: $values.applicationName,
        appId: appJson._id,
        template,
      })

      // Select Correct Application/DB in prep for creating user
      const applicationPkg = await get(
        `/api/applications/${appJson._id}/appPackage`
      )
      const pkg = await applicationPkg.json()
      if (applicationPkg.ok) {
        await store.actions.initialise(pkg)
        await automationStore.actions.fetch()
      } else {
        throw new Error(pkg)
      }

      // Create user
      const user = {
        roleId: $values.roleId,
      }
      const userResp = await api.post(`/api/users/metadata/self`, user)
      await userResp.json()
      $goto(`./${appJson._id}`)
    } catch (error) {
      console.error(error)
      notifications.error(error)
      submitting = false
    }
  }
</script>

<div class="container">
  <div class="sidebar">
    <img src={Logo} alt="budibase icon" />
    <div class="steps">
      {#each steps as component, i}
        <Indicator
          active={$currentStep === i}
          done={i < $currentStep}
          step={i + 1}
        />
      {/each}
    </div>
  </div>
  <div class="body">
    <div class="heading">
      <Heading size="L">Get started with Budibase</Heading>
    </div>
    <div class="step">
      {#each steps as component, i (i)}
        <div class:hidden={$currentStep !== i}>
          <svelte:component
            this={component}
            {template}
            {values}
            {errors}
            {touched}
          />
        </div>
      {/each}
    </div>
    <div class="footer">
      {#if $currentStep > 0}
        <Button medium secondary on:click={() => $currentStep--}>Back</Button>
      {/if}
      {#if $currentStep < steps.length - 1}
        <Button medium cta on:click={() => $currentStep++} disabled={!valid}>
          Next
        </Button>
      {/if}
      {#if $currentStep === steps.length - 1}
        <Button
          medium
          cta
          on:click={createNewApp}
          disabled={!valid || submitting}
        >
          {submitting ? "Loading..." : "Submit"}
        </Button>
      {/if}
    </div>
  </div>
  {#if submitting}
    <div in:fade class="spinner-container">
      <Spinner />
      <span class="spinner-text">Creating your app...</span>
    </div>
  {/if}
</div>

<style>
  .container {
    min-height: 600px;
    display: grid;
    grid-template-columns: 80px 1fr;
    position: relative;
  }
  .sidebar {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    align-items: stretch;
    padding: 40px 0;
    background: var(--grey-1);
  }
  .steps {
    flex: 1 1 auto;
    display: grid;
    border-bottom-left-radius: 0.5rem;
    border-top-left-radius: 0.5rem;
    grid-gap: 30px;
    align-content: center;
  }
  .heading {
    display: flex;
    flex-direction: row;
    align-items: center;
    margin-bottom: 20px;
  }
  .body {
    padding: 40px 60px 40px 60px;
    display: grid;
    align-items: center;
    grid-template-rows: auto 1fr auto;
  }
  .footer {
    display: flex;
    flex-direction: row;
    justify-content: flex-end;
    align-items: center;
    gap: 15px;
  }
  .spinner-container {
    background: var(--background);
    position: absolute;
    border-radius: 5px;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    display: grid;
    justify-items: center;
    align-content: center;
    grid-gap: 50px;
  }
  .spinner-text {
    font-size: 2em;
  }

  .hidden {
    display: none;
  }
  img {
    height: 40px;
    margin-bottom: 20px;
  }
</style>
