<script setup lang="ts">
import { JobStatus } from '#imports'

const {
  modelValue,
  baseId,
  sourceId,
  transition,
  showSourceSelector = true,
} = defineProps<{
  modelValue: boolean
  baseId: string
  sourceId: string
  transition?: string
  showBackBtn?: boolean
  showSourceSelector?: boolean
}>()

const emit = defineEmits(['update:modelValue', 'back'])

const { $api } = useNuxtApp()

const baseURL = $api.instance.defaults.baseURL

const { $state, $poller } = useNuxtApp()

const workspace = useWorkspace()

const { activeWorkspace } = storeToRefs(workspace)

const baseStore = useBase()

const { refreshCommandPalette } = useCommandPalette()

const { loadTables } = baseStore

const { getJobsForBase, loadJobsForBase } = useJobs()

const showGoToDashboardButton = ref(false)

const step = ref(1)

const progressRef = ref()

const lastProgress = ref()

const enableAbort = ref(false)

const goBack = ref(false)

const listeningForUpdates = ref(false)

const hasWarning = ref(false)

const syncSource = ref({
  id: '',
  type: 'Airtable',
  details: {
    syncInterval: '15mins',
    syncDirection: 'Airtable to NocoDB',
    syncRetryCount: 1,
    apiKey: '',
    appId: '',
    shareId: '',
    syncSourceUrlOrId: '',
    options: {
      syncViews: true,
      syncData: true,
      syncRollup: false,
      syncLookup: true,
      syncFormula: false,
      syncAttachment: true,
      syncUsers: false,
    },
  },
})

const sourceSelectorRef = ref()

const customSourceId = computed(() => {
  return sourceSelectorRef.value?.customSourceId || sourceId
})

const onLog = (data: { message: string }) => {
  progressRef.value?.pushProgress(data.message, 'progress')
  lastProgress.value = { msg: data.message, status: 'progress' }

  if (data.message.startsWith('WARNING: ')) {
    hasWarning.value = true
  }
}

const onStatus = async (status: JobStatus, data?: any) => {
  lastProgress.value = { msg: data?.message, status }

  if (status === JobStatus.COMPLETED) {
    showGoToDashboardButton.value = true
    await loadTables()
    progressRef.value?.pushProgress('Done!', status)
    refreshCommandPalette()
    if (activeWorkspace.value?.id) {
      workspace.loadWorkspace(activeWorkspace.value.id)
    }
    // TODO: add tab of the first table
  } else if (status === JobStatus.FAILED) {
    await loadTables()
    goBack.value = true
    progressRef.value?.pushProgress(data.error.message, status)
    refreshCommandPalette()
  }
}

const validators = computed(() => ({
  'details.apiKey': [fieldRequiredValidator()],
  'details.syncSourceUrlOrId': [fieldRequiredValidator()],
}))

const dialogShow = computed({
  get: () => modelValue,
  set: (v) => emit('update:modelValue', v),
})

const useForm = Form.useForm

const { validateInfos } = useForm(syncSource, validators)

const disableImportButton = computed(
  () =>
    !syncSource.value.details.apiKey ||
    !syncSource.value.details.syncSourceUrlOrId ||
    sourceSelectorRef.value?.selectedSource?.ncItemDisabled,
)

const isLoading = ref(false)

async function saveAndSync() {
  isLoading.value = true
  await createOrUpdate()
  await sync()
}

async function createOrUpdate() {
  try {
    const { id, ...payload } = syncSource.value

    if (id !== '') {
      await $api.internal.postOperation(
        activeWorkspace.value!.id,
        baseId,
        {
          operation: 'syncSourceUpdate',
          syncId: id,
        },
        payload,
      )
    } else {
      syncSource.value = await $api.internal.postOperation(
        activeWorkspace.value!.id,
        baseId,
        {
          operation: 'syncSourceCreate',
          sourceId: customSourceId.value,
        },
        payload,
      )
    }
  } catch (e: any) {
    message.error(await extractSdkResponseErrorMsg(e))
  }
}

async function listenForUpdates(id?: string) {
  if (listeningForUpdates.value) return

  listeningForUpdates.value = true

  await loadJobsForBase(baseId)

  const jobs = await getJobsForBase(baseId)

  const job = id
    ? { id }
    : jobs
        // sort by created_at desc (latest first)
        .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())
        .find((j) => j.base_id === baseId && j.status !== JobStatus.COMPLETED && j.status !== JobStatus.FAILED)

  if (!job) {
    listeningForUpdates.value = false
    return
  }

  $poller.subscribe(
    { id: job.id },
    (data: {
      id: string
      status?: string
      data?: {
        error?: {
          message: string
        }
        message?: string
        result?: any
      }
    }) => {
      if (data.status !== 'close') {
        step.value = 2
        if (data.status) {
          onStatus(data.status as JobStatus, data.data)
        } else {
          onLog(data.data as any)
        }
      } else {
        listeningForUpdates.value = false
        isLoading.value = false
      }
    },
  )
}

async function loadSyncSrc() {
  const data: any = await $api.internal.getOperation(activeWorkspace.value!.id, baseId, {
    operation: 'syncSourceList',
    sourceId: customSourceId.value,
  })

  const { list: srcs } = data

  if (srcs && srcs[0]) {
    srcs[0].details = srcs[0].details || {}
    syncSource.value = migrateSync(srcs[0])
    syncSource.value.details.syncSourceUrlOrId =
      srcs[0].details.appId && srcs[0].details.appId.length > 0 ? srcs[0].details.syncSourceUrlOrId : srcs[0].details.shareId
    listenForUpdates()
  } else {
    syncSource.value = {
      id: '',
      type: 'Airtable',
      details: {
        syncInterval: '15mins',
        syncDirection: 'Airtable to NocoDB',
        syncRetryCount: 1,
        apiKey: '',
        appId: '',
        shareId: '',
        syncSourceUrlOrId: '',
        options: {
          syncViews: true,
          syncData: true,
          syncRollup: false,
          syncLookup: true,
          syncFormula: false,
          syncAttachment: true,
          syncUsers: false,
        },
      },
    }
  }
}

async function sync() {
  try {
    const jobData: any = await $api.internal.postOperation(
      activeWorkspace.value!.id,
      baseId,
      {
        operation: 'atImportTrigger',
        syncId: syncSource.value.id,
      },
      {},
    )
    listeningForUpdates.value = false
    listenForUpdates(jobData.id)
  } catch (e: any) {
    message.error(await extractSdkResponseErrorMsg(e))
  }
}

function migrateSync(src: any) {
  if (!src.details?.options) {
    src.details.options = {
      syncViews: false,
      syncData: true,
      syncRollup: false,
      syncLookup: true,
      syncFormula: false,
      syncAttachment: true,
    }
    src.details.options.syncViews = src.syncViews
    delete src.syncViews
  }

  return src
}

watch(
  () => syncSource.value.details.syncSourceUrlOrId,
  (v) => {
    if (syncSource.value.details) {
      const m = v && v.match(/(exp|shr).{14}/g)
      syncSource.value.details.shareId = m ? m[0] : ''
      const m2 = v && v.match(/(app).{14}/g)
      syncSource.value.details.appId = m2 ? m2[0] : ''
    }
  },
)

onMounted(async () => {
  if (syncSource.value.id) {
    listenForUpdates()
  }
  await loadSyncSrc()
})

const isInProgress = computed(() => {
  return !lastProgress.value || ![JobStatus.COMPLETED, JobStatus.FAILED].includes(lastProgress.value?.status)
})

const detailsIsShown = ref(false)
const collapseKey = ref('')
</script>

<template>
  <a-modal
    v-model:visible="dialogShow"
    class="!top-[25vh]"
    :class="{ active: dialogShow }"
    :closable="false"
    :transition-name="transition"
    :keyboard="step !== 2"
    :mask-closable="step !== 2"
    width="448px"
    wrap-class-name="nc-modal-airtable-import"
    hide
    @keydown.esc="dialogShow = false"
  >
    <div class="text-base font-weight-bold flex items-center gap-4 mb-6" @dblclick="enableAbort = true">
      <GeneralIcon icon="airtable" class="w-6 h-6" />

      <span v-if="step === 1" class="flex-1">
        {{ $t('title.quickImportAirtable') }}
      </span>
      <span v-else-if="isInProgress" class="flex-1"> {{ `${$t('labels.importingFromAirtable')}...` }} </span>
      <span v-else class="flex-1"> {{ $t('labels.airtableBaseImported') }} </span>

      <template v-if="step === 1">
        <a
          href="https://nocodb.com/docs/product-docs/bases/import-base-from-airtable#get-airtable-credentials"
          class="!text-nc-content-gray-subtle2 text-sm font-weight-500 ml-auto"
          target="_blank"
          rel="noopener"
        >
          Docs
        </a>
      </template>

      <div v-else-if="step === 2" class="flex items-center gap-2">
        <nc-button type="text" size="xs" class="ml-auto" @click="detailsIsShown = !detailsIsShown">
          {{ detailsIsShown ? 'Hide' : 'Show' }} Details
          <GeneralIcon icon="chevronDown" class="ml-2 transition-all transform" :class="{ 'rotate-180': detailsIsShown }" />
        </nc-button>
        <NcButton v-if="!isInProgress" icon-only type="text" size="xs" @click.stop="dialogShow = false">
          <template #icon>
            <GeneralIcon icon="close" class="text-nc-content-gray-subtle2" />
          </template>
        </NcButton>
      </div>
    </div>

    <div v-if="step === 1">
      <a-form
        ref="form"
        :model="syncSource"
        name="quick-import-airtable-form"
        layout="vertical"
        class="m-0 !text-nc-content-gray"
      >
        <a-form-item v-bind="validateInfos['details.apiKey']" class="!my-5">
          <div class="flex items-end">
            <label class="text-nc-content-gray text-sm"> {{ $t('labels.personalAccessToken') }} </label>
            <a
              href="https://nocodb.com/docs/product-docs/bases/import-base-from-airtable#get-airtable-credentials"
              class="!text-brand text-sm ml-auto"
              target="_blank"
              rel="noopener"
            >
              {{ $t('labels.whereToFind') }}
            </a>
          </div>

          <a-input-password
            v-model:value="syncSource.details.apiKey"
            placeholder="Enter your Airtable Personal Access Token"
            class="!rounded-lg mt-2 nc-input-api-key nc-input-shadow !text-nc-content-gray"
          >
            <template #iconRender="isVisible">
              <GeneralIcon :icon="!isVisible ? 'ncEye' : 'ncEyeOff'" />
            </template>
          </a-input-password>
        </a-form-item>

        <a-form-item v-bind="validateInfos['details.syncSourceUrlOrId']" class="!my-5">
          <label class="text-nc-content-gray text-sm"> {{ `${$t('labels.sharedBase')} ID/URL` }} </label>
          <a-input
            v-model:value="syncSource.details.syncSourceUrlOrId"
            placeholder="Paste the Base URL or Base ID from Airtable"
            class="!rounded-lg !mt-2 nc-input-shared-base nc-input-shadow !text-nc-content-gray"
          />
        </a-form-item>

        <div class="my-5">
          <NcListSourceSelector
            ref="sourceSelectorRef"
            :base-id="baseId"
            :source-id="sourceId"
            :show-source-selector="showSourceSelector"
            force-layout="vertical"
          />
        </div>

        <nc-button type="text" size="small" @click="collapseKey = !collapseKey ? 'advanced-settings' : ''">
          {{ $t('title.advancedSettings') }}
          <GeneralIcon
            icon="chevronDown"
            class="ml-2 !transition-all !transform"
            :class="{ '!rotate-180': collapseKey === 'advanced-settings' }"
          />
        </nc-button>

        <a-collapse v-model:active-key="collapseKey" ghost class="nc-import-collapse">
          <a-collapse-panel key="advanced-settings">
            <div class="mb-2">
              <NcCheckbox v-model:checked="syncSource.details.options.syncData">{{ $t('labels.importData') }}</NcCheckbox>
            </div>

            <div class="my-2">
              <NcCheckbox v-model:checked="syncSource.details.options.syncViews">
                {{ $t('labels.importSecondaryViews') }}
              </NcCheckbox>
            </div>

            <div class="my-2">
              <NcCheckbox v-model:checked="syncSource.details.options.syncRollup">
                {{ $t('labels.importRollupColumns') }}
              </NcCheckbox>
            </div>

            <div class="my-2">
              <NcCheckbox v-model:checked="syncSource.details.options.syncLookup">
                {{ $t('labels.importLookupColumns') }}
              </NcCheckbox>
            </div>

            <div class="my-2">
              <NcCheckbox v-model:checked="syncSource.details.options.syncAttachment">
                {{ $t('labels.importAttachmentColumns') }}
              </NcCheckbox>
            </div>

            <div class="my-2">
              <NcCheckbox v-model:checked="syncSource.details.options.syncFormula" disabled>
                {{ $t('labels.importFormulaColumns') }}
              </NcCheckbox>
            </div>
          </a-collapse-panel>
        </a-collapse>
      </a-form>
    </div>

    <div v-if="step === 2">
      <GeneralProgressPanel v-show="detailsIsShown" ref="progressRef" class="w-full h-[200px]" />
      <div v-show="!detailsIsShown" class="flex items-center gap-2">
        <template v-if="isInProgress">
          <GeneralLoader size="large" class="!mr-1" />
          <span class="truncate">
            {{ lastProgress?.msg ?? '---' }}
          </span>
        </template>
        <template v-else-if="lastProgress?.status === JobStatus.FAILED">
          <NcAlert
            align="center"
            type="error"
            show-icon
            message-class="!text-sm"
            description-class="!text-small !leading-[18px]"
            :copy-text="lastProgress?.msg"
            :message="$t('msg.error.importError')"
            :description="$t('msg.error.anErrorOccuredWhileAirtableBaseImport')"
          />
        </template>
        <div v-else class="flex flex-col gap-3">
          <div class="flex items-center gap-3">
            <GeneralIcon icon="checkFill" class="text-white w-4 h-4" />
            <span> {{ $t('msg.airtableImportSuccess') }} </span>
          </div>
          <div v-if="hasWarning" class="text-yellow-500">{{ $t('msg.airtableImportWarning') }}</div>
        </div>
      </div>

      <div v-if="!isInProgress" class="text-right mt-5">
        <nc-button v-if="lastProgress?.status === JobStatus.FAILED" size="small" @click="step = 1"> Retry import </nc-button>
        <nc-button v-else size="small" @click="dialogShow = false"> Go to base </nc-button>
      </div>
    </div>

    <template #footer>
      <div v-if="step === 1" class="flex justify-between mt-5">
        <nc-button
          key="back"
          type="text"
          size="small"
          @click="
            () => {
              dialogShow = false
              emit('back')
            }
          "
        >
          <GeneralIcon v-if="showBackBtn" icon="chevronLeft" class="mr-1" />

          {{ showBackBtn ? $t('general.back') : $t('general.cancel') }}
        </nc-button>

        <nc-button
          key="submit"
          v-e="['c:sync-airtable:save-and-sync']"
          type="primary"
          class="nc-btn-airtable-import"
          size="small"
          :loading="isLoading"
          :disabled="disableImportButton"
          @click="saveAndSync"
        >
          {{ $t('activity.import') }} Base
        </nc-button>
      </div>
    </template>
  </a-modal>
</template>

<style lang="scss" scoped>
.nc-import-collapse :deep(.ant-collapse-header) {
  display: none !important;
}

.nc-import-collapse :deep(.ant-collapse-content-box) {
  @apply !pb-0 !pt-2 !pr-0.2;
}

.nc-input-api-key {
  :deep(.ant-input-password-icon) {
    @apply !text-current !hover:text-current;
  }
}

.nc-input-api-key,
.nc-input-shared-base {
  @apply !text-nc-content-gray;
  input {
    @apply !text-nc-content-gray;
  }
}
</style>

<style>
.nc-modal-airtable-import .ant-modal-footer {
  @apply !border-none p-0;
}
.nc-modal-airtable-import .ant-collapse-content-box {
  padding-left: 6px;
}
</style>
