import { useState, memo, useMemo } from 'react'
import PropTypes from 'prop-types'
import {
  Typography,
  Button,
  Tag,
  Badge,
  Flex,
  Card,
  Divider,
  Popover
} from 'antd'
import TimeDisplay from './TimeDisplay'
import EditIcon from '../../Icons/EditIcon'
import PropertyChanges from './PropertyChanges'
import XMarkIcon from '../../Icons/XMarkIcon'
import ObjectDisplay from './ObjectDisplay'
import BinIcon from '../../Icons/BinIcon'
import NoteItem from './NoteItem'
import PlusIcon from '../../Icons/PlusIcon'
import InfoCircleIcon from '../../Icons/InfoCircleIcon'
import ExclamationOctagonIcon from '../../Icons/ExclamationOctagonIcon'
import CheckCircleIcon from '../../Icons/CheckCircleIcon'
import BellIcon from '../../Icons/BellIcon'
import { getModelByName } from '../../../database/ObjectModels'

const { Text, Paragraph } = Typography

const getNotificationMessage = (type, metadata) => {
  const objectType = metadata?.objectType ?? metadata?.note?.parentType
  const model = objectType ? getModelByName(objectType) : null
  const objectLabel = model?.label?.toLowerCase() ?? 'object'

  switch (type) {
    case 'editObject':
      return `A ${objectLabel} was updated.`
    case 'deleteObject':
      return `A ${objectLabel} was deleted.`
    case 'newNote':
      return 'A new note was added.'
    case 'info':
      return 'You have a new notification.'
    case 'error':
      return 'An error occurred.'
    case 'success':
      return 'Action completed successfully.'
    default:
      return 'You have a new notification.'
  }
}

const getParagraphProps = (largeSpacing) => ({
  ellipsis: {
    rows: 2,
    expandable: true,
    symbol: 'Show more'
  },
  style: {
    margin: largeSpacing ? '10px 0 0 0' : 0,
    lineHeight: 1.7,
    marginBottom: 2
  }
})

const NotificationBody = memo(function NotificationBody({
  type,
  message,
  metadata,
  largeSpacing
}) {
  const paragraph = getParagraphProps(largeSpacing)

  switch (type) {
    case 'editObject':
      if (metadata?.old || metadata?.new) {
        return (
          <PropertyChanges
            type={metadata?.objectType ?? 'unknown'}
            value={{
              old: metadata.old,
              new: metadata.new
            }}
          />
        )
      }
      return <Paragraph {...paragraph}>{message}</Paragraph>
    case 'newNote':
      return (
        <NoteItem
          note={metadata?.note}
          showCard={false}
          showCreatedAt={false}
          showChildNotes={false}
          showActions={false}
          showInfo={false}
          showBody={true}
          showFooter={false}
          largeSpacing={largeSpacing}
        />
      )
    case 'deleteObject':
      return <Paragraph {...paragraph}>{message}</Paragraph>
    case 'info':
    case 'error':
    case 'success':
      return <Paragraph {...paragraph}>{message}</Paragraph>
    default:
      return <Paragraph {...paragraph}>{message}</Paragraph>
  }
})

NotificationBody.propTypes = {
  type: PropTypes.string,
  message: PropTypes.string,
  metadata: PropTypes.object,
  largeSpacing: PropTypes.bool
}

const NotificationDetails = memo(function NotificationDetails({
  type,
  message,
  metadata,
  largeSpacing
}) {
  const paragraph = getParagraphProps(largeSpacing)
  const object = metadata?.object
  const user = metadata?.user
  const objectType = metadata?.objectType
  const note = metadata?.note

  switch (type) {
    case 'editObject':
      return (
        <Flex align='center' gap='small'>
          <Text {...paragraph} type='secondary'>
            Object:
          </Text>
          <ObjectDisplay
            object={object}
            objectType={objectType}
            showHyperlink={true}
            showSpotlight={true}
          />
          <Text {...paragraph} style={{ marginLeft: '5px' }} type='secondary'>
            User:
          </Text>
          <ObjectDisplay
            object={user}
            objectType='user'
            showHyperlink={true}
            showSpotlight={true}
          />
        </Flex>
      )
    case 'newNote':
      return (
        <Paragraph {...paragraph}>
          <NoteItem
            note={note}
            showCard={false}
            showCreatedAt={false}
            showChildNotes={false}
            showActions={false}
            showInfo={false}
            showBody={false}
            largeSpacing={largeSpacing}
          />
        </Paragraph>
      )
    case 'deleteObject':
      return (
        <Flex align='center' gap='small'>
          <Text {...paragraph} type='secondary'>
            Object:
          </Text>
          <ObjectDisplay
            object={object}
            objectType={objectType}
            showHyperlink={true}
            showSpotlight={true}
          />
          <Text {...paragraph} style={{ marginLeft: '5px' }} type='secondary'>
            User:
          </Text>
          <ObjectDisplay
            object={user}
            objectType='user'
            showHyperlink={true}
            showSpotlight={true}
          />
        </Flex>
      )
    case 'info':
    case 'error':
    case 'success':
      return <Paragraph {...paragraph}>{message}</Paragraph>
    default:
      return <Paragraph {...paragraph}>{message}</Paragraph>
  }
})

NotificationDetails.propTypes = {
  type: PropTypes.string,
  message: PropTypes.string,
  metadata: PropTypes.object,
  largeSpacing: PropTypes.bool
}

const Notification = ({
  notification,
  onMarkAsRead,
  onDelete,
  showCard = true,
  showDelete = true,
  showExtraInfo = true,
  inlineIcon = false,
  largeSpacing = false,
  showSince = true,
  showDetails = true,
  largeTitleSpacing = false
}) => {
  const [deleting, setDeleting] = useState(false)

  const getNotificationTagIcon = (type) => {
    switch (type) {
      case 'info':
        return <InfoCircleIcon />
      case 'editObject':
        return <EditIcon />
      case 'newNote':
        return <PlusIcon />
      case 'deleteObject':
        return <BinIcon />
      case 'error':
        return <ExclamationOctagonIcon />
      case 'success':
        return <CheckCircleIcon />
      default:
        return <BellIcon />
    }
  }

  const getNotificationIcon = (type, metadata) => {
    if (['editObject', 'newNote', 'deleteObject'].includes(type)) {
      const objectType = metadata?.objectType ?? metadata?.note?.parentType
      const model = objectType ? getModelByName(objectType) : null
      if (model?.icon) {
        const Icon = model.icon
        return <Icon style={{ fontSize: 17 }} />
      }
    }

    return getNotificationTagIcon(type)
  }

  const getNotificationTag = (type) => {
    const icon = getNotificationTagIcon(type)
    switch (type) {
      case 'info':
        return (
          <Tag color='blue' icon={icon} style={{ margin: 0 }}>
            Info
          </Tag>
        )
      case 'editObject':
        return (
          <Tag color='blue' icon={icon} style={{ margin: 0 }}>
            Edit
          </Tag>
        )
      case 'deleteObject':
        return (
          <Tag color='error' icon={icon} style={{ margin: 0 }}>
            Delete
          </Tag>
        )
      case 'newNote':
        return (
          <Tag color='green' icon={icon} style={{ margin: 0 }}>
            Note
          </Tag>
        )
      case 'error':
        return (
          <Tag color='red' icon={icon} style={{ margin: 0 }}>
            Error
          </Tag>
        )
      case 'success':
        return (
          <Tag color='green' icon={icon} style={{ margin: 0 }}>
            Success
          </Tag>
        )
      default:
        return (
          <Tag color='default' icon={icon} style={{ margin: 0 }}>
            Default
          </Tag>
        )
    }
  }

  const handleMarkAsRead = () => {
    if (onMarkAsRead && !notification.read) {
      onMarkAsRead(notification._id)
    }
  }

  const handleDelete = async (e) => {
    e.stopPropagation()
    if (onDelete) {
      setDeleting(true)
      try {
        await onDelete(notification._id)
      } finally {
        setDeleting(false)
      }
    }
  }

  const message = useMemo(
    () => getNotificationMessage(notification.type, notification.metadata),
    [notification.type, notification.metadata]
  )

  const notificationDetails = useMemo(
    () => (
      <NotificationDetails
        type={notification.type}
        message={message}
        metadata={notification.metadata}
        largeSpacing={largeSpacing}
      />
    ),
    [notification.type, message, notification.metadata, largeSpacing]
  )

  const notificationBody = useMemo(
    () => (
      <NotificationBody
        type={notification.type}
        message={message}
        metadata={notification.metadata}
        largeSpacing={largeSpacing}
      />
    ),
    [notification.type, message, notification.metadata, largeSpacing]
  )

  const content = (
    <Flex align='start' gap='small' style={{ width: '100%' }}>
      <Flex vertical style={{ flex: 1, width: '100%' }} gap='12px'>
        <Flex vertical gap={largeTitleSpacing ? '10px' : '7px'}>
          <Flex justify='space-between' align='center'>
            <Flex
              align='center'
              gap={largeTitleSpacing ? 'middle' : '8px'}
              style={{ minWidth: 0 }}
            >
              {inlineIcon &&
                getNotificationIcon(notification.type, notification.metadata)}

              <Text
                strong={!notification.read}
                ellipsis
                style={{ minWidth: 0 }}
              >
                {notification.title}
              </Text>
            </Flex>
            <Flex align='center' gap='5px' style={{ marginLeft: 10 }}>
              {showDetails && (
                <Popover
                  arrow={false}
                  trigger={['hover', 'click']}
                  placement='bottomLeft'
                  content={
                    <div style={{ marginLeft: '6px' }}>
                      {notificationDetails}
                    </div>
                  }
                >
                  <Button
                    type='text'
                    size='small'
                    onClick={handleMarkAsRead}
                    disabled={deleting}
                    icon={
                      <InfoCircleIcon
                        style={{ fontSize: 14, marginTop: 2.5, marginLeft: 0 }}
                      />
                    }
                  ></Button>
                </Popover>
              )}
              {showDelete && (
                <Badge
                  dot={!notification.read}
                  offset={[0, 3]}
                  style={{ minWidth: 0 }}
                >
                  <Button
                    type='text'
                    size='small'
                    loading={deleting}
                    disabled={deleting}
                    icon={
                      <XMarkIcon
                        style={{
                          fontSize: 12,
                          marginBottom: 4.5,
                          marginLeft: 0
                        }}
                      />
                    }
                    onClick={handleDelete}
                  />
                </Badge>
              )}
            </Flex>
          </Flex>

          {notificationBody}
        </Flex>
        {showExtraInfo && (
          <>
            <Divider style={{ margin: largeSpacing ? '10px 0' : 0 }} />
            <Flex justify='space-between' align='center'>
              {getNotificationTag(notification.type)}
              <TimeDisplay
                dateTime={notification.createdAt}
                showSince={showSince}
              />
            </Flex>
          </>
        )}
      </Flex>
    </Flex>
  )

  return showCard ? (
    <Card
      size='small'
      style={{
        cursor: 'pointer',
        transition: 'background-color 0.2s',
        backgroundColor: notification.metadata?.note?.noteType?.color
          ? notification.metadata.note.noteType.color + '26'
          : undefined
      }}
      onClick={handleMarkAsRead}
      styles={{ body: { padding: '10px 12px 12px 12px' } }}
    >
      {content}
    </Card>
  ) : (
    <div style={{ marginTop: '-1px', width: '100%' }}>{content}</div>
  )
}

Notification.propTypes = {
  notification: PropTypes.shape({
    _id: PropTypes.string.isRequired,
    title: PropTypes.string.isRequired,
    type: PropTypes.oneOf([
      'info',
      'warning',
      'error',
      'success',
      'default',
      'editObject',
      'deleteObject',
      'newNote'
    ]).isRequired,
    read: PropTypes.bool.isRequired,
    createdAt: PropTypes.string.isRequired,
    metadata: PropTypes.object
  }).isRequired,
  onMarkAsRead: PropTypes.func,
  onDelete: PropTypes.func,
  showCard: PropTypes.bool,
  showDelete: PropTypes.bool,
  showExtraInfo: PropTypes.bool,
  inlineIcon: PropTypes.bool,
  largeSpacing: PropTypes.bool,
  showSince: PropTypes.bool,
  showDetails: PropTypes.bool,
  largeTitleSpacing: PropTypes.bool
}

export default Notification
