import { mdiChartTimelineVariant, mdiClose, mdiViewDashboard } from '@mdi/js'
import Head from 'next/head'
import { useRouter } from 'next/router'
import React, { ReactElement, useCallback, useEffect, useMemo, useState } from 'react'
import axios from 'axios'
import BaseButton from '../../components/BaseButton'
import CardBox from '../../components/CardBox'
import CardBoxModal from '../../components/CardBoxModal'
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 ElementSettings = {
  color: string
  backgroundColor: string
  border: string
  icon: string
}

type ProjectElementItem = {
  id: string
  elementType: string
  name: string
  settings: ElementSettings
}

type PlatformElementOption = {
  elementType: string
  name: string
  defaults: ElementSettings
}

type PageElementRecord = {
  id: string
  element_type?: string
  name?: string
  style_json?: string
  content_json?: string
}

const FALLBACK_ELEMENT_TYPES = [
  'nav_button',
  'spot',
  'description',
  'tooltip',
  'gallery',
  'carousel',
  'logo',
  'video_player',
  'popup',
]

const FALLBACK_DEFAULTS_BY_TYPE: Record<string, ElementSettings> = {
  nav_button: { color: '#111827', backgroundColor: '#ffffff', border: '1px solid #d1d5db', icon: 'mdiArrowRight' },
  spot: { color: '#111827', backgroundColor: '#fde68a', border: '1px solid #f59e0b', icon: 'mdiMapMarker' },
  description: { color: '#111827', backgroundColor: '#f3f4f6', border: '1px solid #d1d5db', icon: 'mdiTextBox' },
  tooltip: { color: '#ffffff', backgroundColor: '#1f2937', border: '1px solid #1f2937', icon: 'mdiTooltipText' },
  gallery: { color: '#111827', backgroundColor: '#ffffff', border: '1px solid #d1d5db', icon: 'mdiImageMultiple' },
  carousel: { color: '#111827', backgroundColor: '#ffffff', border: '1px solid #d1d5db', icon: 'mdiViewCarousel' },
  logo: { color: '#111827', backgroundColor: '#ffffff', border: '1px solid #d1d5db', icon: 'mdiImage' },
  video_player: { color: '#ffffff', backgroundColor: '#111827', border: '1px solid #111827', icon: 'mdiPlayCircle' },
  popup: { color: '#111827', backgroundColor: '#ffffff', border: '1px solid #d1d5db', icon: 'mdiOpenInNew' },
}

const parseJsonObject = (value?: string): Record<string, any> => {
  if (!value) return {}
  try {
    const parsed = JSON.parse(value)
    return typeof parsed === 'object' && parsed !== null ? parsed : {}
  } catch {
    return {}
  }
}

const toElementLabel = (value: string) =>
  value
    .split('_')
    .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
    .join(' ')

const createLocalId = () => {
  if (typeof window !== 'undefined' && window.crypto?.randomUUID) {
    return window.crypto.randomUUID()
  }

  return `pe_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`
}

const normalizeSettings = (
  settings: Partial<ElementSettings> | undefined,
  fallback: ElementSettings,
): ElementSettings => ({
  color: settings?.color || fallback.color,
  backgroundColor: settings?.backgroundColor || fallback.backgroundColor,
  border: settings?.border || fallback.border,
  icon: settings?.icon || fallback.icon,
})

const PagesElementsListPage = () => {
  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 hasCreatePermission = hasPermission(currentUser, 'CREATE_PAGE_ELEMENTS')
  const hasUpdatePermission = hasPermission(currentUser, 'UPDATE_PAGE_ELEMENTS')
  const hasDeletePermission = hasPermission(currentUser, 'DELETE_PAGE_ELEMENTS')

  const [projectName, setProjectName] = useState('')
  const [isLoadingProject, setIsLoadingProject] = useState(false)
  const [isLoadingElements, setIsLoadingElements] = useState(false)
  const [errorMessage, setErrorMessage] = useState('')

  const [themeConfig, setThemeConfig] = useState<Record<string, any>>({})
  const [selectedElements, setSelectedElements] = useState<ProjectElementItem[]>([])
  const [platformElements, setPlatformElements] = useState<PlatformElementOption[]>([])

  const [isAddDropdownOpen, setIsAddDropdownOpen] = useState(false)
  const [newElementType, setNewElementType] = useState('')

  const [isSettingsModalActive, setIsSettingsModalActive] = useState(false)
  const [activeElement, setActiveElement] = useState<ProjectElementItem | null>(null)
  const [elementName, setElementName] = useState('')
  const [color, setColor] = useState('')
  const [backgroundColor, setBackgroundColor] = useState('')
  const [border, setBorder] = useState('')
  const [icon, setIcon] = useState('')

  const [isSaving, setIsSaving] = useState(false)

  const loadData = useCallback(async () => {
    if (!routeProjectId) {
      setProjectName('')
      setThemeConfig({})
      setSelectedElements([])
      setPlatformElements([])
      setNewElementType('')
      return
    }

    setIsLoadingProject(true)
    setIsLoadingElements(true)
    setErrorMessage('')

    try {
      const [projectResponse, pageElementsResponse] = await Promise.all([
        axios.get(`/projects/${routeProjectId}`),
        axios.get('/page_elements?limit=1000&page=0&sort=desc&field=updatedAt'),
      ])

      const project = projectResponse?.data || {}
      setProjectName(project?.name || '')

      const parsedThemeConfig = parseJsonObject(project?.theme_config_json)
      const rawProjectElements = Array.isArray(parsedThemeConfig?.pageElements) ? parsedThemeConfig.pageElements : []

      const normalizedProjectElements: ProjectElementItem[] = rawProjectElements
        .filter((item: any) => item && item.elementType)
        .map((item: any) => {
          const elementType = String(item.elementType)
          const defaults = FALLBACK_DEFAULTS_BY_TYPE[elementType] || {
            color: '#111827',
            backgroundColor: '#ffffff',
            border: '1px solid #d1d5db',
            icon: '',
          }

          return {
            id: String(item.id || createLocalId()),
            elementType,
            name: String(item.name || toElementLabel(elementType)),
            settings: normalizeSettings(item.settings || {}, defaults),
          }
        })

      const rows = Array.isArray(pageElementsResponse?.data?.rows) ? pageElementsResponse.data.rows : []
      const optionsMap = new Map<string, PlatformElementOption>()

      FALLBACK_ELEMENT_TYPES.forEach((type) => {
        optionsMap.set(type, {
          elementType: type,
          name: toElementLabel(type),
          defaults: FALLBACK_DEFAULTS_BY_TYPE[type],
        })
      })

      rows.forEach((row: PageElementRecord) => {
        const elementType = String(row.element_type || '').trim()
        if (!elementType || optionsMap.has(elementType)) return

        const rowStyle = parseJsonObject(row.style_json)
        const rowContent = parseJsonObject(row.content_json)
        const fallback = FALLBACK_DEFAULTS_BY_TYPE[elementType] || {
          color: '#111827',
          backgroundColor: '#ffffff',
          border: '1px solid #d1d5db',
          icon: '',
        }

        optionsMap.set(elementType, {
          elementType,
          name: row.name || toElementLabel(elementType),
          defaults: {
            color: String(rowStyle.color || fallback.color),
            backgroundColor: String(rowStyle.backgroundColor || fallback.backgroundColor),
            border: String(rowStyle.border || fallback.border),
            icon: String(rowContent.icon || fallback.icon),
          },
        })
      })

      const normalizedOptions = Array.from(optionsMap.values())
      setThemeConfig(parsedThemeConfig)
      setSelectedElements(normalizedProjectElements)
      setPlatformElements(normalizedOptions)
    } catch (error: any) {
      const message = error?.response?.data?.message || error?.message || 'Failed to load pages elements.'
      setErrorMessage(message)
      console.error('Failed to load pages elements list:', error)
      setThemeConfig({})
      setSelectedElements([])
      setPlatformElements([])
    } finally {
      setIsLoadingProject(false)
      setIsLoadingElements(false)
    }
  }, [routeProjectId])

  useEffect(() => {
    loadData()
  }, [loadData])

  const selectedTypes = useMemo(() => new Set(selectedElements.map((item) => item.elementType)), [selectedElements])

  const availableToAdd = useMemo(
    () => platformElements.filter((item) => !selectedTypes.has(item.elementType)),
    [platformElements, selectedTypes],
  )

  useEffect(() => {
    if (!availableToAdd.length) {
      setNewElementType('')
      return
    }

    if (!availableToAdd.some((item) => item.elementType === newElementType)) {
      setNewElementType(availableToAdd[0].elementType)
    }
  }, [availableToAdd, newElementType])

  const saveProjectElements = useCallback(
    async (elements: ProjectElementItem[]) => {
      if (!routeProjectId) return

      const nextThemeConfig = {
        ...(themeConfig || {}),
        pageElements: elements.map((item) => ({
          id: item.id,
          elementType: item.elementType,
          name: item.name,
          settings: item.settings,
        })),
      }

      await axios.put(`/projects/${routeProjectId}`, {
        id: routeProjectId,
        data: {
          theme_config_json: JSON.stringify(nextThemeConfig),
        },
      })

      setThemeConfig(nextThemeConfig)
    },
    [routeProjectId, themeConfig],
  )

  const handleAddElement = async () => {
    if (!hasCreatePermission) return

    if (!routeProjectId) {
      setErrorMessage('Please select a project first.')
      return
    }

    if (!newElementType || selectedTypes.has(newElementType)) {
      return
    }

    const selectedOption = platformElements.find((item) => item.elementType === newElementType)
    if (!selectedOption) {
      setErrorMessage('Selected element type is not available.')
      return
    }

    setIsSaving(true)
    setErrorMessage('')

    try {
      const updatedElements = [
        ...selectedElements,
        {
          id: createLocalId(),
          elementType: selectedOption.elementType,
          name: selectedOption.name,
          settings: { ...selectedOption.defaults },
        },
      ]

      await saveProjectElements(updatedElements)
      setSelectedElements(updatedElements)
      setIsAddDropdownOpen(false)
    } catch (error: any) {
      const message = error?.response?.data?.message || error?.message || 'Failed to add element.'
      setErrorMessage(message)
      console.error('Failed to add project element:', error)
    } finally {
      setIsSaving(false)
    }
  }

  const handleRemoveElement = async (item: ProjectElementItem) => {
    if (!hasDeletePermission) return

    const confirmed = window.confirm(`Remove ${item.name || toElementLabel(item.elementType)} from this project?`)
    if (!confirmed) return

    setIsSaving(true)
    setErrorMessage('')

    try {
      const updatedElements = selectedElements.filter((existing) => existing.id !== item.id)
      await saveProjectElements(updatedElements)
      setSelectedElements(updatedElements)
    } catch (error: any) {
      const message = error?.response?.data?.message || error?.message || 'Failed to remove element.'
      setErrorMessage(message)
      console.error('Failed to remove project element:', error)
    } finally {
      setIsSaving(false)
    }
  }

  const openSettings = (item: ProjectElementItem) => {
    if (!hasUpdatePermission) return

    setActiveElement(item)
    setElementName(item.name || toElementLabel(item.elementType))
    setColor(item.settings.color)
    setBackgroundColor(item.settings.backgroundColor)
    setBorder(item.settings.border)
    setIcon(item.settings.icon)
    setIsSettingsModalActive(true)
  }

  const closeSettings = () => {
    setIsSettingsModalActive(false)
    setActiveElement(null)
  }

  const saveSettings = async () => {
    if (!activeElement || !hasUpdatePermission) return

    setIsSaving(true)
    setErrorMessage('')

    try {
      const updatedElements = selectedElements.map((item) => {
        if (item.id !== activeElement.id) return item

        return {
          ...item,
          name: elementName,
          settings: {
            color,
            backgroundColor,
            border,
            icon,
          },
        }
      })

      await saveProjectElements(updatedElements)
      setSelectedElements(updatedElements)
      closeSettings()
    } catch (error: any) {
      const message = error?.response?.data?.message || error?.message || 'Failed to save element settings.'
      setErrorMessage(message)
      console.error('Failed to update project element settings:', error)
    } finally {
      setIsSaving(false)
    }
  }

  const constructorHref = routeProjectId ? `/constructor?projectId=${routeProjectId}` : '/constructor'

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

        <p className='mb-6 text-sm font-semibold'>
          {isLoadingProject ? 'Loading project...' : projectName || 'No project selected'}
        </p>

        <CardBox className='mb-6' cardBoxClassName='flex flex-wrap items-start gap-3'>
          {hasCreatePermission && (
            <div className='relative'>
              <BaseButton
                color='info'
                label='Add element'
                onClick={() => setIsAddDropdownOpen((prev) => !prev)}
                disabled={isSaving}
              />
              {isAddDropdownOpen && (
                <div className='absolute z-20 mt-2 w-72 rounded border border-gray-200 bg-white p-3 shadow-lg dark:bg-dark-900 dark:border-dark-700'>
                  <select
                    className='mb-2 w-full rounded border border-gray-300 px-2 py-2 dark:bg-dark-800'
                    value={newElementType}
                    onChange={(event) => setNewElementType(event.target.value)}
                    disabled={isSaving || availableToAdd.length === 0}
                  >
                    {platformElements.map((item) => (
                      <option key={item.elementType} value={item.elementType} disabled={selectedTypes.has(item.elementType)}>
                        {item.name}
                        {selectedTypes.has(item.elementType) ? ' (selected)' : ''}
                      </option>
                    ))}
                  </select>
                  <BaseButton
                    color='success'
                    label={isSaving ? 'Adding...' : 'Add'}
                    onClick={handleAddElement}
                    disabled={isSaving || !availableToAdd.length || !newElementType || selectedTypes.has(newElementType)}
                  />
                </div>
              )}
            </div>
          )}

          <BaseButton
            color='lightDark'
            icon={mdiViewDashboard}
            href={constructorHref}
            label='To Constructor'
            disabled={!routeProjectId}
          />
        </CardBox>

        {errorMessage ? <CardBox className='mb-6 text-sm text-red-600'>{errorMessage}</CardBox> : null}

        <CardBox>
          <h3 className='mb-3 text-lg font-semibold'>Selected pages elements</h3>

          {isLoadingElements ? (
            <p className='text-sm text-gray-500'>Loading elements...</p>
          ) : selectedElements.length === 0 ? (
            <p className='text-sm text-gray-500'>No elements selected yet.</p>
          ) : (
            <div className='space-y-2'>
              {selectedElements.map((item) => (
                <div
                  key={item.id}
                  className='flex items-center justify-between gap-3 rounded border border-gray-200 px-3 py-2 dark:border-dark-700'
                >
                  <button
                    type='button'
                    className='flex-1 text-left'
                    onClick={() => openSettings(item)}
                    disabled={!hasUpdatePermission || isSaving}
                  >
                    <p className='text-sm font-semibold'>{item.name || toElementLabel(item.elementType)}</p>
                    <p className='text-xs text-gray-500'>
                      {toElementLabel(item.elementType)} • icon: {item.settings.icon || '-'}
                    </p>
                  </button>

                  {hasDeletePermission ? (
                    <BaseButton
                      color='danger'
                      icon={mdiClose}
                      small
                      roundedFull
                      onClick={() => handleRemoveElement(item)}
                      disabled={isSaving}
                    />
                  ) : null}
                </div>
              ))}
            </div>
          )}
        </CardBox>
      </SectionMain>

      <CardBoxModal
        title={activeElement ? `Edit ${toElementLabel(activeElement.elementType)}` : 'Element settings'}
        buttonColor='info'
        buttonLabel={isSaving ? 'Saving...' : 'Save settings'}
        isActive={isSettingsModalActive}
        onConfirm={saveSettings}
        onCancel={closeSettings}
        isConfirmDisabled={isSaving || !hasUpdatePermission}
      >
        <div>
          <label className='mb-1 block text-sm font-semibold'>Name</label>
          <input
            className='w-full rounded border border-gray-300 px-2 py-2 dark:bg-dark-800 dark:border-dark-700'
            value={elementName}
            onChange={(event) => setElementName(event.target.value)}
          />
        </div>
        <div>
          <label className='mb-1 block text-sm font-semibold'>Color</label>
          <input
            className='w-full rounded border border-gray-300 px-2 py-2 dark:bg-dark-800 dark:border-dark-700'
            value={color}
            onChange={(event) => setColor(event.target.value)}
            placeholder='#111827'
          />
        </div>
        <div>
          <label className='mb-1 block text-sm font-semibold'>Background</label>
          <input
            className='w-full rounded border border-gray-300 px-2 py-2 dark:bg-dark-800 dark:border-dark-700'
            value={backgroundColor}
            onChange={(event) => setBackgroundColor(event.target.value)}
            placeholder='#ffffff'
          />
        </div>
        <div>
          <label className='mb-1 block text-sm font-semibold'>Border</label>
          <input
            className='w-full rounded border border-gray-300 px-2 py-2 dark:bg-dark-800 dark:border-dark-700'
            value={border}
            onChange={(event) => setBorder(event.target.value)}
            placeholder='1px solid #d1d5db'
          />
        </div>
        <div>
          <label className='mb-1 block text-sm font-semibold'>Icon</label>
          <input
            className='w-full rounded border border-gray-300 px-2 py-2 dark:bg-dark-800 dark:border-dark-700'
            value={icon}
            onChange={(event) => setIcon(event.target.value)}
            placeholder='mdiStar'
          />
        </div>
      </CardBoxModal>
    </>
  )
}

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

export default PagesElementsListPage
