import { mdiChartTimelineVariant } from '@mdi/js'
import axios from 'axios'
import Head from 'next/head'
import { useRouter } from 'next/router'
import React, { ReactElement, useEffect, useMemo, useState } from 'react'
import { toast, ToastContainer } from 'react-toastify'
import BaseButton from '../../components/BaseButton'
import CardBox from '../../components/CardBox'
import FileUploader from '../../components/Uploaders/UploadService'
import LayoutAuthenticated from '../../layouts/Authenticated'
import SectionMain from '../../components/SectionMain'
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
import { getPageTitle } from '../../config'
import { hasPermission } from '../../helpers/userPermissions'
import { useAppSelector } from '../../stores/hooks'

type Project = {
  id: string
  name: string
}

type Asset = {
  id: string
  name: string
  asset_type: 'image' | 'video' | 'audio' | 'file'
  type?:
    | 'icon'
    | 'background_image'
    | 'audio'
    | 'video'
    | 'transition'
    | 'logo'
    | 'favicon'
    | 'document'
    | 'general'
  cdn_url?: string | null
  mime_type?: string | null
}

type AssetSection = {
  key: 'images' | 'backgroundImages' | 'audio' | 'video' | 'transitions' | 'logo'
  label: string
  accept: string
  assetFormat: 'image' | 'video' | 'audio'
  assetCategory: NonNullable<Asset['type']>
  legacyTag: string
}

type UploadQueueItem = {
  id: string
  fileName: string
  progress: number
  status: 'queued' | 'uploading' | 'saving' | 'success' | 'error'
  error?: string
}

const ASSET_SECTIONS: AssetSection[] = [
  { key: 'images', label: 'Icons', accept: 'image/*', assetFormat: 'image', assetCategory: 'icon', legacyTag: 'IMAGE' },
  {
    key: 'backgroundImages',
    label: 'Background Images',
    accept: 'image/*',
    assetFormat: 'image',
    assetCategory: 'background_image',
    legacyTag: 'BACKGROUND',
  },
  { key: 'audio', label: 'Audio', accept: 'audio/*', assetFormat: 'audio', assetCategory: 'audio', legacyTag: 'AUDIO' },
  { key: 'video', label: 'Video', accept: 'video/*', assetFormat: 'video', assetCategory: 'video', legacyTag: 'VIDEO' },
  {
    key: 'transitions',
    label: 'Transitions',
    accept: 'video/*',
    assetFormat: 'video',
    assetCategory: 'transition',
    legacyTag: 'TRANSITION',
  },
  { key: 'logo', label: 'Logo', accept: 'image/*', assetFormat: 'image', assetCategory: 'logo', legacyTag: 'LOGO' },
]

const AssetsTablesPage = () => {
  const router = useRouter()
  const routeProjectId = useMemo(() => {
    const value = router.query.projectId
    if (Array.isArray(value)) return value[0] || ''
    return String(value || '')
  }, [router.query.projectId])

  const { currentUser } = useAppSelector((state) => state.auth)

  const [projects, setProjects] = useState<Project[]>([])
  const [selectedProjectId, setSelectedProjectId] = useState('')
  const [isLoadingProjects, setIsLoadingProjects] = useState(false)
  const [assets, setAssets] = useState<Asset[]>([])
  const [isLoadingAssets, setIsLoadingAssets] = useState(false)
  const [uploadingSections, setUploadingSections] = useState<string[]>([])
  const [uploadQueues, setUploadQueues] = useState<Record<string, UploadQueueItem[]>>({})
  const [expandedUploadedLists, setExpandedUploadedLists] = useState<Record<string, boolean>>({})
  const [deletingAssetId, setDeletingAssetId] = useState<string>('')

  const hasCreatePermission = Boolean(currentUser && hasPermission(currentUser, 'CREATE_ASSETS'))
  const hasDeletePermission = Boolean(currentUser && hasPermission(currentUser, 'DELETE_ASSETS'))

  const loadProjects = async () => {
    setIsLoadingProjects(true)

    try {
      let rows: Project[] = []
      const response = await axios.get('/projects?limit=100&page=0&sort=desc&field=updatedAt')
      rows = Array.isArray(response?.data?.rows) ? response.data.rows : []

      if (rows.length === 0) {
        const autocompleteResponse = await axios.get('/projects/autocomplete?limit=100')
        const autocompleteItems = Array.isArray(autocompleteResponse?.data)
          ? autocompleteResponse.data
          : []
        rows = autocompleteItems.map((item: { id: string; label: string }) => ({
          id: item.id,
          name: item.label,
        }))
      }

      setProjects(rows)

      if (routeProjectId && rows.some((project) => project.id === routeProjectId)) {
        setSelectedProjectId(routeProjectId)
      } else {
        setSelectedProjectId((prev) => {
          if (rows.some((project) => project.id === prev)) return prev
          return rows[0]?.id || ''
        })
      }

    } catch (error: any) {
      console.error('Failed to load projects:', error?.message || error)
      setProjects([])
      setSelectedProjectId('')
      toast('Failed to load projects', { type: 'error', position: 'bottom-center' })
    } finally {
      setIsLoadingProjects(false)
    }
  }

  const loadAssets = async (projectId: string) => {
    if (!projectId) {
      setAssets([])
      return
    }

    setIsLoadingAssets(true)
    try {
      const response = await axios.get(
        `/assets?limit=500&page=0&sort=desc&field=createdAt&project=${projectId}`,
      )
      const rows = Array.isArray(response?.data?.rows) ? response.data.rows : []
      setAssets(rows)
    } catch (error: any) {
      console.error('Failed to load assets:', error?.message || error)
      toast('Failed to load assets', { type: 'error', position: 'bottom-center' })
    } finally {
      setIsLoadingAssets(false)
    }
  }

  useEffect(() => {
    loadProjects()
  }, [routeProjectId])

  useEffect(() => {
    loadAssets(selectedProjectId)
  }, [selectedProjectId])

  const addSectionUpload = (sectionKey: string, items: UploadQueueItem[]) => {
    setUploadQueues((prev) => ({
      ...prev,
      [sectionKey]: [...(prev[sectionKey] || []), ...items],
    }))
  }

  const updateSectionUpload = (sectionKey: string, itemId: string, patch: Partial<UploadQueueItem>) => {
    setUploadQueues((prev) => ({
      ...prev,
      [sectionKey]: (prev[sectionKey] || []).map((item) =>
        item.id === itemId ? { ...item, ...patch } : item,
      ),
    }))
  }

  const markSectionUploading = (sectionKey: string, isUploading: boolean) => {
    setUploadingSections((prev) => {
      if (isUploading) {
        if (prev.includes(sectionKey)) {
          return prev
        }
        return [...prev, sectionKey]
      }

      return prev.filter((key) => key !== sectionKey)
    })
  }

  const toggleUploadedList = (sectionKey: string) => {
    setExpandedUploadedLists((prev) => ({
      ...prev,
      [sectionKey]: !(prev[sectionKey] ?? true),
    }))
  }

  const uploadAssetFile = async (
    section: AssetSection,
    projectId: string,
    itemId: string,
    file: File,
  ) => {
    updateSectionUpload(section.key, itemId, { status: 'uploading', progress: 0 })

    const remoteFile = await FileUploader.uploadChunked(`assets/${projectId}`, file, {}, {
      chunkSize: 5 * 1024 * 1024,
      maxRetries: 3,
      onProgress: (progress: number) => {
        updateSectionUpload(section.key, itemId, { progress, status: 'uploading' })
      },
      onStatus: (status: string) => {
        if (status === 'finalizing') {
          updateSectionUpload(section.key, itemId, { status: 'saving' })
        }
      },
    })

    await axios.post('/assets', {
      data: {
        project: projectId,
        name: file.name,
        asset_type: section.assetFormat,
        type: section.assetCategory,
        cdn_url: remoteFile.publicUrl,
        storage_key: remoteFile.privateUrl,
        mime_type: file.type || null,
        size_mb: Number((file.size / (1024 * 1024)).toFixed(4)),
        is_public: false,
        is_deleted: false,
      },
    })

    updateSectionUpload(section.key, itemId, { status: 'success', progress: 100 })
  }

  const runBatchUpload = async (section: AssetSection, files: File[]) => {
    if (!selectedProjectId) {
      toast('Select a project first', { type: 'warning', position: 'bottom-center' })
      return
    }

    if (!files.length) {
      return
    }

    const queueItems: UploadQueueItem[] = files.map((file) => ({
      id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
      fileName: file.name,
      progress: 0,
      status: 'queued',
    }))

    addSectionUpload(section.key, queueItems)
    markSectionUploading(section.key, true)

    const queue = files.map((file, index) => ({
      file,
      itemId: queueItems[index].id,
    }))
    const maxConcurrent = 2
    let currentIndex = 0
    let failedCount = 0

    const worker = async () => {
      while (currentIndex < queue.length) {
        const nextIndex = currentIndex
        currentIndex += 1
        const item = queue[nextIndex]

        try {
          await uploadAssetFile(section, selectedProjectId, item.itemId, item.file)
        } catch (error: any) {
          const message = error?.response?.data?.message || error?.message || 'Upload failed'
          console.error(`Failed to upload ${item.file.name}:`, error)
          failedCount += 1
          updateSectionUpload(section.key, item.itemId, {
            status: 'error',
            error: message,
          })
        }
      }
    }

    try {
      await Promise.all(Array.from({ length: Math.min(maxConcurrent, queue.length) }, () => worker()))
      await loadAssets(selectedProjectId)
      if (failedCount > 0) {
        toast(`Batch upload finished: ${queue.length - failedCount}/${queue.length} succeeded`, {
          type: 'warning',
          position: 'bottom-center',
        })
      } else {
        toast(`Batch upload finished for ${section.label}`, { type: 'success', position: 'bottom-center' })
      }
    } finally {
      markSectionUploading(section.key, false)
    }
  }

  const handleDeleteAsset = async (assetId: string) => {
    setDeletingAssetId(assetId)

    try {
      await axios.delete(`/assets/${assetId}`)
      toast('Asset deleted', { type: 'success', position: 'bottom-center' })
      await loadAssets(selectedProjectId)
    } catch (error: any) {
      console.error('Failed to delete asset:', error?.message || error)
      toast('Failed to delete asset', { type: 'error', position: 'bottom-center' })
    } finally {
      setDeletingAssetId('')
    }
  }

  const assetsBySection = useMemo(() => {
    return ASSET_SECTIONS.reduce<Record<string, Asset[]>>((acc, section) => {
      acc[section.key] = assets.filter((asset) => {
        if (asset.type) return asset.type === section.assetCategory
        return asset.name?.startsWith(`[${section.legacyTag}] `)
      })
      return acc
    }, {})
  }, [assets])

  return (
    <>
      <Head>
        <title>{getPageTitle('Assets')}</title>
      </Head>
      <SectionMain>
        <SectionTitleLineWithButton icon={mdiChartTimelineVariant} title='Assets' main>
          {''}
        </SectionTitleLineWithButton>

        <p className='mb-6 text-sm font-semibold'>
          {isLoadingProjects
            ? 'Loading project...'
            : projects.find((project) => project.id === selectedProjectId)?.name || 'No project selected'}
        </p>

        <div className='grid grid-cols-1 xl:grid-cols-2 gap-4'>
          {ASSET_SECTIONS.map((section) => {
            const list = assetsBySection[section.key] || []
            const isUploadedListExpanded = expandedUploadedLists[section.key] ?? true

            return (
              <CardBox key={section.key} className='h-full'>
                <div className='flex items-center justify-between gap-3 mb-4'>
                  <h3 className='text-lg font-semibold'>{section.label}</h3>
                  {!hasCreatePermission && (
                    <p className='text-xs text-gray-500'>You do not have upload permission</p>
                  )}
                </div>
                <input
                  type='file'
                  multiple
                  accept={section.accept}
                  className='w-full border border-gray-300 rounded px-2 py-2 mb-3 bg-white dark:bg-dark-800'
                  disabled={uploadingSections.includes(section.key) || !selectedProjectId || !hasCreatePermission}
                  onChange={(event) => {
                    const files = Array.from(event.target.files || [])
                    void runBatchUpload(section, files)
                    event.currentTarget.value = ''
                  }}
                />
                {uploadingSections.includes(section.key) && (
                  <p className='text-sm text-gray-500 mb-3'>
                    Uploading {section.label.toLowerCase()} in chunks...
                  </p>
                )}

                {(uploadQueues[section.key] || []).length > 0 && (
                  <ul className='space-y-1 mb-3 max-h-40 overflow-auto'>
                    {(uploadQueues[section.key] || []).map((item) => (
                      <li key={item.id} className='text-xs border border-gray-200 dark:border-dark-700 rounded p-2'>
                        <div className='flex items-center justify-between gap-2'>
                          <span className='truncate'>{item.fileName}</span>
                          <span>
                            {item.status === 'error'
                              ? 'Error'
                              : item.status === 'success'
                                ? 'Done'
                                : item.status}
                          </span>
                        </div>
                        {item.status !== 'success' && (
                          <div className='w-full h-1 bg-gray-200 rounded mt-1'>
                            <div className='h-1 bg-blue-500 rounded' style={{ width: `${item.progress}%` }} />
                          </div>
                        )}
                        {item.error && <p className='text-red-500 mt-1 truncate'>{item.error}</p>}
                      </li>
                    ))}
                  </ul>
                )}

                {isLoadingAssets && <p className='text-sm text-gray-500'>Loading assets...</p>}

                {!isLoadingAssets && list.length > 0 && (
                  <button
                    type='button'
                    className='mb-2 text-xs underline'
                    onClick={() => toggleUploadedList(section.key)}
                  >
                    {isUploadedListExpanded
                      ? `Hide uploaded ${section.label.toLowerCase()}`
                      : `Show uploaded ${section.label.toLowerCase()} (${list.length})`}
                  </button>
                )}

                {!isLoadingAssets && list.length === 0 && (
                  <p className='text-sm text-gray-500'>No uploaded {section.label.toLowerCase()}.</p>
                )}

                {!isLoadingAssets && list.length > 0 && isUploadedListExpanded && (
                  <ul className='space-y-2'>
                    {list.map((asset) => (
                      <li
                        key={asset.id}
                        className='flex items-center justify-between gap-2 p-2 border border-gray-200 dark:border-dark-700 rounded'
                      >
                        <a
                          href={asset.cdn_url || '#'}
                          target='_blank'
                          rel='noreferrer'
                          className='text-sm underline truncate'
                        >
                          {asset.name.replace(/^\[[^\]]+\]\s*/, '')}
                        </a>
                        <BaseButton
                          color='danger'
                          label='X'
                          small
                          disabled={deletingAssetId === asset.id || !hasDeletePermission}
                          onClick={() => handleDeleteAsset(asset.id)}
                        />
                      </li>
                    ))}
                  </ul>
                )}
              </CardBox>
            )
          })}
        </div>
      </SectionMain>
      <ToastContainer />
    </>
  )
}

AssetsTablesPage.getLayout = function getLayout(page: ReactElement) {
  return <LayoutAuthenticated permission={'READ_ASSETS'}>{page}</LayoutAuthenticated>
}

export default AssetsTablesPage
