import React, { useEffect, useRef, useState } from 'react'
import { type Meta, type StoryObj } from '@storybook/react-vite'

import { DocsTemplate } from '../../../.storybook'
import FileUploader, { type FileType, MIMETypes } from './FileUploader'
import Alert from '../Alerts/Alert'
import { toast } from '../Toast/Toast'

const meta: Meta<typeof FileUploader> = {
  title: 'Components/FileUploader',
  component: FileUploader,
  parameters: {
    design: {
      type: 'figma',
      url: 'https://www.figma.com/design/RvhKD82948FMQnh5MyCi0o/Web-Design-System?node-id=121-338&t=jsd8kEmb1sJfCa2m-0',
    },
    docs: {
      page: () => (
        <DocsTemplate
          figmaLink='https://www.figma.com/design/RvhKD82948FMQnh5MyCi0o/Web-Design-System?node-id=121-338&t=jsd8kEmb1sJfCa2m-0'
          description={
            <span>
              The <code>FileUploader</code> component is a user-friendly
              solution designed to facilitate the uploading of files. It
              provides both a drag-and-drop interface and a button to open a
              file manager, making the upload process straightforward and
              accessible for users. This also supports folder uploads, allowing
              users to select entire folders for upload, with each file in the
              folder processed individually.
            </span>
          }
          infoBullets={[
            <span key='first-bullet'>
              Use the <code>FileUploader</code> component to enable users to
              upload files easily, whether through drag-and-drop or by opening a
              file manager.
            </span>,
            <span key='second-bullet'>
              Integrate the component in sections of the application where file
              uploads are required, such as data import, bulk updates, or
              reporting.
            </span>,
            <span key='third-bullet'>
              To quickly access pre-defined types for the{' '}
              <code>acceptedFileTypes</code> prop, use the{' '}
              <code>MIMETypes</code> enum.
            </span>,
            <span key='fourth-bullet'>
              Users can select entire folders for upload, with each file
              processed individually.
            </span>,
          ]}
        />
      ),
    },
  },
}
export default meta

type Story = StoryObj<typeof FileUploader>

const Template: Story = {
  render: function Render(args) {
    const [files, setFiles] = useState<FileType[]>(args.files ?? [])

    const handleUpload = (updatedFiles: File[]) => {
      if (updatedFiles) {
        setFiles(updatedFiles)
      }
    }

    //// The following code is for the purpose of simulating file uploads ////
    const progressTimers = useRef<{
      [key: string]: ReturnType<typeof setInterval>
    }>({})

    const simulateFileUpload = (file: FileType) => {
      const fileId = `${file.name}-${Date.now()}`
      let progress = 0

      // Random chance of failure (20%)
      const willFail = Math.random() < 0.2

      const timer = setInterval(() => {
        progress += 2 // Will complete in 5 seconds (50 intervals of 2%)

        setFiles((prev) =>
          prev.map((f) =>
            f.name === file.name
              ? {
                  ...f,
                  progress,
                  status: progress >= 100 ? 'success' : 'uploading',
                }
              : f,
          ),
        )

        if (progress >= 100 || (willFail && progress >= 60)) {
          clearInterval(progressTimers.current[fileId])
          delete progressTimers.current[fileId]

          setFiles((prev) =>
            prev.map((f) =>
              f.name === file.name
                ? {
                    ...f,
                    progress: willFail ? progress : 100,
                    status: willFail ? 'error' : 'success',
                  }
                : f,
            ),
          )
        }
      }, 100)

      progressTimers.current[fileId] = timer
    }

    const handleAsyncUpload = (newFiles: File[]) => {
      const newFilesWithStatus: FileType[] = newFiles.map((file) => ({
        ...file,
        name: file.name,
        progress: 0,
        status: 'uploading',
      }))

      setFiles((prev) => [...(prev ?? []), ...newFilesWithStatus])

      // Start upload simulation for new files only
      newFilesWithStatus.forEach((file) => {
        simulateFileUpload(file)
      })
    }

    // Cleanup timers on unmount
    useEffect(() => {
      const currentTimers = progressTimers.current
      return () => {
        Object.values(currentTimers).forEach((timer) => clearInterval(timer))
      }
    }, [])
    //// End of file upload simulation code ////

    return (
      <div style={{ width: 400 }}>
        {args.acceptedFileTypes ? (
          <Alert
            text={
              <span>
                The accepted file types for this story are{' '}
                {args.acceptedFileTypes
                  ?.map((type) =>
                    type
                      .split(',')
                      .map((t) => t.trim())
                      .join(', '),
                  )
                  .join(', ')}
              </span>
            }
            type='info'
            customClass='mb-5'
          />
        ) : null}
        <FileUploader
          {...args}
          controlled={args.controlled ?? true}
          files={files}
          uploadCallout={args.controlled ? handleAsyncUpload : handleUpload}
          removeFileCallout={(file: FileType) => {
            setFiles((prev) => prev.filter((f) => f.name !== file.name))
          }}
        />
      </div>
    )
  },
}

const genericArgs = {
  formLabelProps: { label: 'Upload File' },
}

export const Basic: Story = {
  ...Template,
  args: genericArgs,
}

export const DownloadCallout: Story = {
  ...Template,
  args: {
    formLabelProps: genericArgs.formLabelProps,
    downloadCallout: () => {
      toast({ message: 'Download callout clicked', type: 'success' })
    },
  },
}

export const WithoutLabel: Story = {
  ...Template,
  args: {},
}

export const WithAcceptedFileTypes: Story = {
  ...Template,
  args: {
    formLabelProps: genericArgs.formLabelProps,
    acceptedFileTypes: [MIMETypes.Media],
  },
}

export const Disabled: Story = {
  ...Template,
  args: {
    formLabelProps: genericArgs.formLabelProps,
    disabled: true,
  },
}

export const MultipleFiles: Story = {
  ...Template,
  args: {
    formLabelProps: genericArgs.formLabelProps,
    acceptMultiple: true,
  },
}

export const MultipleFilesWithAcceptedTypes: Story = {
  ...Template,
  args: {
    formLabelProps: genericArgs.formLabelProps,
    acceptMultiple: true,
    acceptedFileTypes: [MIMETypes.Spreadsheets, MIMETypes.Images],
  },
}

export const FilesPopulated: Story = {
  ...Template,
  args: {
    ...genericArgs,
    acceptMultiple: true,
    files: [
      new File(['This is a test text file'], 'test1.txt', {
        type: 'text/plain',
      }),
      new File(['This is a test text file'], 'test2.txt', {
        type: 'text/plain',
      }),
      new File(['This is a test text file'], 'test3.txt', {
        type: 'text/plain',
      }),
    ],
  },
}

// TODO: This story will be changed to simulate the "status" of file uploads instead of showcasing the "controlled" state. Once all implementations are "controlled", this story will be updated.
export const Controlled: Story = {
  ...Template,
  args: {
    ...genericArgs,
    acceptMultiple: true,
    controlled: true,
  },
}

export const noFileRemoval: Story = {
  ...Template,
  args: {
    ...genericArgs,
    noFileRemoval: true,
  },
}
