<template>
  <section class="h-full relative flex items-center justify-center">
    <n-card class="max-w-lg" title="About" v-if="!userStore.user">
      <p>Welcome to the <b>Solar Drive</b></p>
      <p>We help you upload, collect, and share files with ease in mind.</p>
      <p>To continue, login first.</p>

      <p class="mt-4 opacity-75 text-xs">
        <span v-if="version == null">Loading...</span>
        <span v-else>
          v{{ version.version }} @
          {{ version.commit.substring(0, 6) }}
          {{ version.updatedAt }}
        </span>
      </p>
    </n-card>
    <n-card class="max-w-2xl" title="Upload to Solar Network" v-else>
      <template #header-extra>
        <div class="flex gap-2 items-center">
          <p>Advance Mode</p>
          <n-switch v-model:value="modeAdvanced" size="small" />
        </div>
      </template>

      <div class="mb-3">
        <n-select
          v-model:value="filePool"
          :options="pools ?? []"
          :render-label="renderPoolSelectLabel"
          :render-tag="renderSingleSelectTag"
          value-field="id"
          label-field="name"
          placeholder="Select a file pool to upload"
          clearable
          size="large"
        />
      </div>

      <n-collapse-transition :show="modeAdvanced">
        <n-card title="Advance Options" size="small" class="mb-3">
          <div>
            <p class="pl-1 mb-0.5">File Password</p>
            <n-input
              v-model:value="filePass"
              :disabled="!currentFilePool?.allow_encryption"
              placeholder="Enter password to protect the file"
              show-password-toggle
              size="large"
              type="password"
              class="mb-2"
            />
            <p class="pl-1 text-xs opacity-75 mt-[-4px]">Only available for Stellar Program and certian file pool.</p>
          </div>
        </n-card>
      </n-collapse-transition>

      <n-upload
        multiple
        directory-dnd
        with-credentials
        show-preview-button
        list-type="image"
        show-download-button
        :custom-request="customRequest"
        :custom-download="customDownload"
        :create-thumbnail-url="createThumbnailUrl"
        @preview="customPreview"
      >
        <n-upload-dragger>
          <div style="margin-bottom: 12px">
            <n-icon size="48" :depth="3">
              <cloud-upload-round />
            </n-icon>
          </div>
          <n-text style="font-size: 16px"> Click or drag a file to this area to upload </n-text>
          <n-p depth="3" style="margin: 8px 0 0 0">
            Strictly prohibit from uploading sensitive information. For example, your bank card PIN
            or your credit card expiry date.
          </n-p>
        </n-upload-dragger>
      </n-upload>

      <p class="mt-4 opacity-75 text-xs">
        <span v-if="version == null">Loading...</span>
        <span v-else>
          v{{ version.version }} @
          {{ version.commit.substring(0, 6) }}
          {{ version.updatedAt }}
        </span>
      </p>
    </n-card>
  </section>
</template>

<script setup lang="ts">
import {
  NCard,
  NUpload,
  NUploadDragger,
  NIcon,
  NText,
  NP,
  NInput,
  NSwitch,
  NSelect,
  NTag,
  NCollapseTransition,
  type UploadCustomRequestOptions,
  type UploadSettledFileInfo,
  type SelectOption,
  type SelectRenderTag,
  type UploadFileInfo,
} from 'naive-ui'
import { computed, h, onMounted, ref } from 'vue'
import { CloudUploadRound } from '@vicons/material'
import { useUserStore } from '@/stores/user'
import type { SnFilePool } from '@/types/pool'

import * as tus from 'tus-js-client'

const userStore = useUserStore()

const version = ref<any>(null)
async function fetchVersion() {
  const resp = await fetch('/api/version')
  version.value = await resp.json()
}
onMounted(() => fetchVersion())

type SnFilePoolOption = SnFilePool & any

const pools = ref<SnFilePoolOption[] | undefined>()
async function fetchPools() {
  const resp = await fetch('/api/pools')
  pools.value = await resp.json()
}
onMounted(() => fetchPools())

const renderSingleSelectTag: SelectRenderTag = ({ option }) => {
  return h(
    'div',
    {
      style: {
        display: 'flex',
        alignItems: 'center',
      },
    },
    [option.name as string],
  )
}

function renderPoolSelectLabel(option: SelectOption & SnFilePool, selected: boolean) {
  return h(
    'div',
    {
      style: {
        padding: '8px 2px',
      },
    },
    [
      h('div', null, [option.name as string]),
      h(
        'div',
        {
          style: {
            display: 'flex',
            gap: '0.25rem',
            marginTop: '2px',
            marginLeft: '-2px',
            marginRight: '-2px',
          },
        },
        [
          option.public_usable &&
            h(
              NTag,
              {
                type: 'info',
                size: 'small',
                round: true,
              },
              { default: () => 'Public Shared' },
            ),
          option.public_indexable &&
            h(
              NTag,
              {
                type: 'success',
                size: 'small',
                round: true,
              },
              { default: () => 'Public Indexable' },
            ),
          option.allow_encryption &&
            h(
              NTag,
              {
                type: 'warning',
                size: 'small',
                round: true,
              },
              { default: () => 'Allow Encryption' },
            ),
          option.allow_anonymous &&
            h(
              NTag,
              {
                type: 'info',
                size: 'small',
                round: true,
              },
              { default: () => 'Allow Anonymous' },
            ),
        ],
      ),
    ],
  )
}

const modeAdvanced = ref(false)

const filePool = ref<string | null>(null)
const filePass = ref<string>('')

const currentFilePool = computed(() => {
  if (!filePool.value) return null
  return pools.value?.find((pool) => pool.id === filePool.value) ?? null
})

function customRequest({
  file,
  data,
  headers,
  withCredentials,
  action,
  onFinish,
  onError,
  onProgress,
}: UploadCustomRequestOptions) {
  const requestHeaders: Record<string, string> = {}
  if (filePool.value) requestHeaders['X-FilePool'] = filePool.value
  if (filePass.value) requestHeaders['X-FilePass'] = filePass.value
  const upload = new tus.Upload(file.file, {
    endpoint: '/api/tus',
    retryDelays: [0, 3000, 5000, 10000, 20000],
    metadata: {
      filename: file.name,
      filetype: file.type ?? 'application/octet-stream',
    },
    headers: {
      ...requestHeaders,
      ...headers,
    },
    onError: function (error) {
      console.error('[DRIVE] Upload failed:', error)
      onError()
    },
    onProgress: function (bytesUploaded, bytesTotal) {
      onProgress({ percent: (bytesUploaded / bytesTotal) * 100 })
    },
    onSuccess: function (payload) {
      const rawInfo = payload.lastResponse.getHeader('x-fileinfo')
      const jsonInfo = JSON.parse(rawInfo as string)
      console.log('[DRIVE] Upload successful: ', jsonInfo)
      file.url = `/api/files/${jsonInfo.id}`
      file.type = jsonInfo.mime_type
      onFinish()
    },
    onBeforeRequest: function (req) {
      const xhr = req.getUnderlyingObject()
      xhr.withCredentials = withCredentials
    },
  })
  upload.findPreviousUploads().then(function (previousUploads) {
    if (previousUploads.length) {
      upload.resumeFromPreviousUpload(previousUploads[0])
    }
    upload.start()
  })
}

function createThumbnailUrl(
  _file: File | null,
  fileInfo: UploadSettledFileInfo,
): string | undefined {
  if (!fileInfo) return undefined
  return fileInfo.url ?? undefined
}

function customDownload(file: UploadFileInfo) {
  const { url, name } = file
  if (!url)
    return
  window.open(url.replace('/api', ''), '_blank')
}

function customPreview(file: UploadFileInfo, detail: { event: MouseEvent }) {
  detail.event.preventDefault()
  const { url, type } = file
  if (!url) return
  window.open(url.replace('/api', ''), '_blank')
}
</script>
