/**
 * Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { IFileURLs } from "@streamlit/protobuf"

import { UploadFileInfo } from "~lib/components/widgets/FileUploader/UploadFileInfo"
import { FileUploadClient } from "~lib/FileUploadClient"
import { WidgetInfo } from "~lib/WidgetStateManager"

interface CreateUploadFileParams {
  getNextLocalFileId: () => number
  addFiles: (files: UploadFileInfo[]) => void
  updateFile: (id: number, fileInfo: UploadFileInfo) => void
  uploadClient: FileUploadClient
  element: WidgetInfo
  onUploadProgress: (e: ProgressEvent, id: number) => void
  onUploadComplete: (id: number, fileURLs: IFileURLs) => void
}

export const createUploadFileHandler =
  ({
    getNextLocalFileId,
    addFiles,
    updateFile,
    uploadClient,
    element,
    onUploadProgress,
    onUploadComplete,
  }: CreateUploadFileParams) =>
  (fileURLs: IFileURLs, file: File): void => {
    // Create an UploadFileInfo for this file and add it to our state.
    // For directory uploads, prefer the webkitRelativePath so we preserve
    // the original directory structure in the displayed file name.
    const fileName = file.webkitRelativePath || file.name

    const abortController = new AbortController()
    const uploadingFileInfo = new UploadFileInfo(
      fileName,
      file.size,
      getNextLocalFileId(),
      {
        type: "uploading",
        abortController,
        progress: 1,
      },
      file
    )
    addFiles([uploadingFileInfo])

    uploadClient
      .uploadFile(
        {
          formId: "", // TODO[kajarnec] fix this probably with uploadFile refactoring
          ...element,
        },
        fileURLs.uploadUrl as string,
        file,
        e => onUploadProgress(e, uploadingFileInfo.id),
        abortController.signal
      )
      .then(() => onUploadComplete(uploadingFileInfo.id, fileURLs))
      .catch(err => {
        // If this was an abort error, we don't show the user an error -
        // the cancellation was in response to an action they took.
        if (!(err instanceof DOMException && err.name === "AbortError")) {
          updateFile(
            uploadingFileInfo.id,
            uploadingFileInfo.setStatus({
              type: "error",
              errorMessage: err ? err.toString() : "Unknown error",
            })
          )
        }
      })
  }
