<script lang="tsx">
import {
  computed, defineComponent, ref,
  watch, type PropType,
} from 'vue'
import createApi from '../../stubs/api-generator-file'
import type ApiInterface from '../../types/api-interface'
import DefaultButton from './DefaultButton.vue'
import ModalWidndow from '../modal-window/modal-window.vue'
import { type ModalWindowInterface } from '../modal-window/types'
import type { FileData } from './upload-area-types'

import './assets/styles.scss'

export default defineComponent({
  props: {
    api: {
      type: Object as PropType<ApiInterface>,
      required: true,
    },
    /**
     * Отправлять запрос на удаление файла или нет
     */
    doDeleteRequest: {
      type: Boolean,
      default: true,
    },
    /**
     * Эндпоинд, на который будут отправляться запросы на добавление / удаление файла
     */
    endpoint: {
      type: String,
      default: '/file/',
    },
    /**
     * Массив файлов
     */
    files: {
      type: Array as PropType<FileData[]>,
      required: true,
      default: () => [],
    },
    /**
     * Параметр для загрузки файла
     */
    fileUploadKey: {
      type: String,
      default: 'file',
    },
    /**
     * Форматы, которые можно загрузить
     */
    formats: {
      type: Array as PropType<string[]>,
      default: () => ['.jpg', '.png', '.pdf'],
    },
    /**
     * Состояние инпута (вкл/выкл)
     */
    isDisabled: {
      type: Boolean,
      default: false,
    },
    /**
     * При значении false для загрузки будет доступен только 1 файл
     */
    isMultiple: {
      type: Boolean,
      default: true,
    },
    /**
     * Отображать заблокированный инпут в случае, когда разрешено выбирать только один файл
     */
    isShowDisabledInput: {
      type: Boolean,
      default: false,
    },
    /**
     * Функция, которой можно привести массив файлов к нужному формату
     */
    itemConverter: {
      type: Function as PropType<(file: any) => FileData>,
    },
    /**
     * Заголовок
     */
    label: {
      type: String,
      default: 'Нажмите или перетащите файл в эту область, чтобы загрузить документ',
    },
    /**
     * Максимальное число файлов
     */
    maximumFiles: {
      type: Number,
      default: undefined,
    },
    /**
     * Функция на добавление файлов
     */
    onUpload: {
      type: Function as PropType<(files: FileData[]) => void>,
      required: true,
    },
  },

  setup(props) {
    const isError = ref(false)
    const isLoading = ref(false)
    const isDisabledInput = ref(props.isDisabled || (!props.isMultiple && props.files.length > 0))
    const fileName = ref('')
    const progressBar = ref(0)
    const status = ref('')
    const loadedTotal = ref('')
    const loaded = ref('')
    const arrFiles = ref<any[]>([])
    const title = computed(() => props.files[0]?.name ?? 'Выберите файл')
    const modalWindow = ref<ModalWindowInterface | null>(null)

    const api = props.api || createApi()

    const isImage = (file: FileData) => {
      const lowercaseExtension = file?.extension?.toLowerCase()
      return ['jpg', 'png', 'jpeg'].indexOf(lowercaseExtension) > -1
    }

    const convertToMB = (number: number) => {
      if (number === 0) {
        return `${loadedTotal.value}`
      }
      return `${+(number / 1048576).toFixed(2)}\u00A0MB`
    }

    function progressHandler(event: ProgressEvent<XMLHttpRequestEventTarget>) {
      loadedTotal.value = `${+(event.total / 1048576).toFixed(2)}\u00A0MB`
      loaded.value = `(${event.loaded} байт)`
      const percent = (event.loaded / event.total) * 100
      progressBar.value = Math.round(percent)
      status.value = `${Math.round(percent)}%`
    }

    function completeHandler() {
      progressBar.value = 100
      isLoading.value = false
    }

    function errorHandler(event: ProgressEvent<XMLHttpRequestEventTarget>) {
      loadedTotal.value = convertToMB(event.total)
      isError.value = true
    }

    function abortHandler() {
      isError.value = true
    }

    const uploadFiles = async (files: string[] | FileList | File[]) => {
      if (files instanceof FileList) files = Array.from(files)

      const results = await Promise.all((files as Array<File | string>).map((file) => {
        fileName.value = typeof file === 'string' ? file : file.name
        const formData = new FormData()
        formData.append(props.fileUploadKey, file)

        if (!api) {
          isError.value = true
          throw new Error('API is not defined')
        }
        return api
          .post(props.endpoint, formData, {
            onUploadProgress: (event) => progressHandler(event as unknown as ProgressEvent<XMLHttpRequestEventTarget>),
          })
          .then((value) => {
            completeHandler()
            return value
          })
          .catch((error) => {
            abortHandler()
            throw error
          })
      }))

      const defaultItemConverter = (response: any) => ({
        id: response.data.id,
        name: response.data.name,
        file: response.data.file,
        extension: response.data.extension,
        size: response.data.size,
      })

      const uploadedFiles = results.map(props.itemConverter || defaultItemConverter)

      if (props.onUpload) props.onUpload([...(props.isMultiple ? props.files : []), ...uploadedFiles])
    }

    const removeFile = async (file: FileData) => {
      if (props.doDeleteRequest) {
        api.delete(`${props.endpoint}/${file.id}`)
      }
      const splicedFiles = [...props.files].filter(({ id }) => id !== file.id)
      arrFiles.value = splicedFiles

      if (props.onUpload) props.onUpload(arrFiles.value)

      const input = document.querySelector('input[type=file]') as HTMLFormElement | null
      if (input) input.value = ''

      isDisabledInput.value = false
    }

    const removeFiles = async (files: FileData[]) => {
      if (props.isDisabled) return

      if (props.doDeleteRequest) {
        files.forEach(({ id }) => api.delete(`${props.endpoint}/${id}`))
      }

      if (props.onUpload) props.onUpload([])

      isError.value = false
      isLoading.value = false
    }

    const exitAddFiles = () => {
      isLoading.value = false
      isError.value = false
    }

    const getExtension = (filename: string) => filename.split('.').pop()?.toLowerCase() || ''

    const filterFiles = (files: FileList) => {
      const filesArray: File[] = []

      for (let i = 0; i < files.length; i++) {
        const file = files[i]
        const extension = `.${getExtension(file.name)}`
        if (props.formats.includes(extension)) filesArray.push(file)
      }

      return filesArray
    }

    const onChange = (event: Event) => {
      const { files } = event.target as HTMLInputElement
      if (!files) return

      const filesArray = filterFiles(files)
      if (!filesArray.length) return

      isLoading.value = true
      isError.value = false

      arrFiles.value = filesArray
      uploadFiles(filesArray)
    }

    const isInputVisible = computed(() => {
      if (props.isMultiple || props.isShowDisabledInput) return true
      return !props.files.length
    })

    const onOpenConfirmModal = (event: Event, data: FileData | FileData[]) => {
      event.preventDefault()
      if (props.isDisabled) return
      if (!modalWindow.value) throw new Error('Modal confirmation window does not exist')

      const isMany = Array.isArray(data)
      const callback = isMany ? () => removeFiles(data) : () => removeFile(data)

      modalWindow.value.open({
        head: isMany ? 'Удалить все файлы' : 'Удалить файл',
        body: isMany ? 'Вы уверены, что хотите удалить все файлы?' : 'Вы уверены, что хотите удалить файл?',
        footer: [
          <DefaultButton label='Отмена' type='secondary' onClick={modalWindow.value.close} />,
          <DefaultButton
            label='Удалить'
            type='critical'
            onClick={() => callback().then(modalWindow.value?.close)} />,
        ],
      })
    }

    const isLimitedFiles = () => {
      if (props.isDisabled || (!props.isMultiple && props.files.length > 0)) isDisabledInput.value = true
      else if (props.maximumFiles && !props.isDisabled) {
        isDisabledInput.value = props.files.length >= props.maximumFiles
      }
    }
    isLimitedFiles()

    watch(() => props.files.length, () => isLimitedFiles())
    watch(() => props.maximumFiles, () => isLimitedFiles())

    return () => (
      <div class="ark-ui-upload-area">
        {isInputVisible.value && !props.isDisabled && (
          <div class={`ark-ui-upload-drop-area ${isDisabledInput.value ? 'disabled' : ''}`}>
            <input
              type="file"
              title={title.value}
              accept={props.formats.join(',')}
              onChange={onChange}
              disabled={isDisabledInput.value}
              multiple={props.isMultiple}
            />
            <div class="ark-ui-upload-drop-area-icon"/>
            <p class="ark-ui-upload-drop-area-text">{props.label}</p>
            <p class="ark-ui-upload-drop-area-description">
              Вы можете загрузить файлы форматов: {props.formats.join(', ')}
            </p>
          </div>
        )}

        { (props.files.length > 0 || isLoading.value)
          && <div class="ark-ui-upload-files-box">
            {isInputVisible.value && <div class="ark-ui-upload-actions">
              {/* не показывать количество файлов при одиночном выборе */}
              {!props.isShowDisabledInput && <p class="ark-ui-upload-file-count">Файлов: {props.files.length}</p>}

              {/* не показывать "удалить всё" при заблокированном инпуте и при одиночном выборе */}
              {(!props.isDisabled || (props.isShowDisabledInput && !isDisabledInput.value)) && (
                <button
                  class="ark-ui-upload-button-delete"
                  onClick={(e) => onOpenConfirmModal(e, props.files)}
                >
                  Удалить все
                </button>
              )}
            </div>}
            <div class="ark-ui-upload-files">
              {props.files.map((file, index) => {
                const background = isImage(file)
                  ? `background-image: url(${file.file})`
                  : 'background-size: 24px 24px; background-repeat: no-repeat;'

                return (
                  <a
                    class="ark-ui-upload-file"
                    href={file.file}
                    target="_blank"
                    style={`--tooltip: '${file.name} ${convertToMB(file.size)}';`}
                  >
                    <div
                      class="ark-ui-upload-file-image"
                      style={background}
                    >
                      {!props.isDisabled && (
                        <div
                          class="ark-ui-upload-file-remove"
                          // onClick={(event: Event) => removeFile(event, index)}
                          onClick={(e) => onOpenConfirmModal(e, props.files[index])}
                        />
                      )}
                    </div>
                    <div class="ark-ui-upload-file-name">
                      {file.name.length > 14 ? `${file.name.slice(0, 11)}..${getExtension(file.name)}` : file.name}
                    </div>
                  </a>
                )
              })}

              { arrFiles.value && isLoading.value
                && arrFiles.value.map((file) => {
                  const background = isImage(file)
                    ? `background-image: url(${file.file})`
                    : 'background-size: 24px 24px; background-repeat: no-repeat;'

                  return (
                    <a
                      class={`ark-ui-upload-file ark-ui-upload-downloadable-files ${isError.value ? 'error' : ''}`}
                      style={`--tooltip: '${isError.value ? 'При загрузке данных произошла ошибка' : ''}';`}
                    >
                      <div class="ark-ui-upload-wrap">
                        <div
                          class={`ark-ui-upload-file-image ${file.type.includes('image') ? 'error-image' : 'error-file'}`}
                          style={background}
                        >
                          <div
                            class="ark-ui-upload-file-remove"
                            onClick={exitAddFiles}
                          />
                        </div>
                      </div>
                      <div
                        class="ark-ui-upload-file-name"
                        style={`${(isLoading.value || isError.value) ? 'word-break: normal;' : ''}`}
                      >
                        { isError.value ? 'Ошибка загрузки' : 'Идет загрузка...' }
                      </div>
                    </a>
                  )
                })
              }

            </div>
            <ModalWidndow ref={modalWindow} />
          </div> }

      </div>
    )
  },
})
</script>
