/**
 * NotificationCenter - Notification list panel with unread count and actions
 * @countermeasure/web-components/display/notification-center
 */

import {
  type NotificationCenterConfig,
  type NotificationItem,
  type NotificationLevel,
} from './types'
import { resolveContainer, uniqueId } from '../../primitives/utils'
import { setSafeIconHtml } from '../../utils/sanitize'

export type { NotificationCenterConfig, NotificationItem, NotificationLevel } from './types'

/** Default SVG icons per notification level */
const LEVEL_ICONS: Record<NotificationLevel, string> = {
  info: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>',
  success:
    '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>',
  warning:
    '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>',
  error:
    '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>',
}

/** Format a Date as a relative time string */
function formatRelativeTime(date: Date): string {
  const now = Date.now()
  const diff = now - date.getTime()
  const seconds = Math.floor(diff / 1000)
  const minutes = Math.floor(seconds / 60)
  const hours = Math.floor(minutes / 60)
  const days = Math.floor(hours / 24)

  if (seconds < 60) {
    return 'Just now'
  }
  if (minutes < 60) {
    return `${String(minutes)}m ago`
  }
  if (hours < 24) {
    return `${String(hours)}h ago`
  }
  if (days < 7) {
    return `${String(days)}d ago`
  }
  return date.toLocaleDateString()
}

/** Tracked state for each rendered notification item */
interface ItemHandlers {
  el: HTMLDivElement
  dismissHandler: (() => void) | null
  actionHandler: (() => void) | null
  dismissBtn: HTMLButtonElement | null
  actionBtn: HTMLButtonElement | null
}

export class NotificationCenter {
  public readonly el: HTMLDivElement

  private readonly centerId: string
  private readonly notifications: NotificationItem[]
  private readonly maxVisible: number
  private readonly emptyMessage: string
  private readonly onMarkRead: ((id: string) => void) | undefined
  private readonly onMarkAllRead: (() => void) | undefined
  private readonly onDismiss: ((id: string) => void) | undefined
  private readonly onClearAll: (() => void) | undefined

  private listEl: HTMLDivElement
  private badgeEl: HTMLSpanElement
  private emptyEl: HTMLDivElement
  private markAllReadBtn: HTMLButtonElement
  private clearAllBtn: HTMLButtonElement

  private readonly itemHandlersMap = new Map<string, ItemHandlers>()
  private readonly markAllReadHandler: () => void
  private readonly clearAllHandler: () => void

  constructor(config: NotificationCenterConfig) {
    this.centerId = uniqueId('cmm-notification-center')
    this.notifications = config.notifications ? [...config.notifications] : []
    this.maxVisible = config.maxVisible ?? 50
    this.emptyMessage = config.emptyMessage ?? 'No notifications'
    this.onMarkRead = config.onMarkRead
    this.onMarkAllRead = config.onMarkAllRead
    this.onDismiss = config.onDismiss
    this.onClearAll = config.onClearAll

    // Create root element
    this.el = document.createElement('div')
    this.el.id = this.centerId
    this.el.className = this.buildClassName(config.className)
    this.el.setAttribute('role', 'region')
    this.el.setAttribute('aria-label', 'Notification center')

    // Build header
    const headerEl = document.createElement('div')
    headerEl.className = 'cmm-notification-center__header'

    const headerLeft = document.createElement('div')
    headerLeft.className = 'cmm-notification-center__header-left'

    const titleEl = document.createElement('span')
    titleEl.className = 'cmm-notification-center__title'
    titleEl.textContent = 'Notifications'
    headerLeft.appendChild(titleEl)

    this.badgeEl = document.createElement('span')
    this.badgeEl.className = 'cmm-notification-center__badge'
    this.badgeEl.setAttribute('aria-label', 'Unread count')
    headerLeft.appendChild(this.badgeEl)

    headerEl.appendChild(headerLeft)

    const actionsEl = document.createElement('div')
    actionsEl.className = 'cmm-notification-center__actions'

    this.markAllReadBtn = document.createElement('button')
    this.markAllReadBtn.className = 'cmm-notification-center__action-btn'
    this.markAllReadBtn.type = 'button'
    this.markAllReadBtn.textContent = 'Mark all read'

    this.markAllReadHandler = () => {
      this.markAllAsRead()
    }
    this.markAllReadBtn.addEventListener('click', this.markAllReadHandler)
    actionsEl.appendChild(this.markAllReadBtn)

    this.clearAllBtn = document.createElement('button')
    this.clearAllBtn.className = 'cmm-notification-center__action-btn'
    this.clearAllBtn.type = 'button'
    this.clearAllBtn.textContent = 'Clear all'

    this.clearAllHandler = () => {
      this.clearAll()
    }
    this.clearAllBtn.addEventListener('click', this.clearAllHandler)
    actionsEl.appendChild(this.clearAllBtn)

    headerEl.appendChild(actionsEl)
    this.el.appendChild(headerEl)

    // Build list
    this.listEl = document.createElement('div')
    this.listEl.className = 'cmm-notification-center__list'
    this.listEl.setAttribute('role', 'list')
    this.el.appendChild(this.listEl)

    // Build empty state
    this.emptyEl = document.createElement('div')
    this.emptyEl.className = 'cmm-notification-center__empty'
    this.emptyEl.textContent = this.emptyMessage

    // Render initial state
    this.render()

    // Mount if container provided
    if (config.container !== undefined) {
      const parent = resolveContainer(config.container)
      parent.appendChild(this.el)
    }
  }

  /** Add a notification to the top of the list */
  addNotification(item: NotificationItem): void {
    this.notifications.unshift(item)

    // Trim to maxVisible
    while (this.notifications.length > this.maxVisible) {
      const removed = this.notifications.pop()
      if (removed !== undefined) {
        this.removeItemEl(removed.id)
      }
    }

    this.render()
  }

  /** Remove a notification by id */
  removeNotification(id: string): void {
    const index = this.notifications.findIndex(n => n.id === id)
    if (index !== -1) {
      this.notifications.splice(index, 1)
    }
    this.removeItemEl(id)
    this.render()

    if (this.onDismiss !== undefined) {
      this.onDismiss(id)
    }
  }

  /** Mark a single notification as read */
  markAsRead(id: string): void {
    const notification = this.notifications.find(n => n.id === id)
    if (notification !== undefined) {
      notification.read = true
    }

    const handlers = this.itemHandlersMap.get(id)
    if (handlers !== undefined) {
      handlers.el.classList.remove('cmm-notification-center__item--unread')
    }

    this.updateBadge()

    if (this.onMarkRead !== undefined) {
      this.onMarkRead(id)
    }
  }

  /** Mark all notifications as read */
  markAllAsRead(): void {
    for (const notification of this.notifications) {
      notification.read = true
    }

    for (const [, handlers] of this.itemHandlersMap) {
      handlers.el.classList.remove('cmm-notification-center__item--unread')
    }

    this.updateBadge()

    if (this.onMarkAllRead !== undefined) {
      this.onMarkAllRead()
    }
  }

  /** Remove all notifications */
  clearAll(): void {
    this.cleanupAllItems()
    this.notifications.length = 0
    this.render()

    if (this.onClearAll !== undefined) {
      this.onClearAll()
    }
  }

  /** Get the count of unread notifications */
  getUnreadCount(): number {
    let count = 0
    for (const notification of this.notifications) {
      if (notification.read !== true) {
        count += 1
      }
    }
    return count
  }

  /** Remove the element from the DOM and clean up all event listeners */
  destroy(): void {
    this.markAllReadBtn.removeEventListener('click', this.markAllReadHandler)
    this.clearAllBtn.removeEventListener('click', this.clearAllHandler)
    this.cleanupAllItems()
    this.el.remove()
  }

  private render(): void {
    this.cleanupAllItems()
    this.listEl.textContent = ''

    if (this.notifications.length === 0) {
      this.listEl.appendChild(this.emptyEl)
    } else {
      const visibleItems = this.notifications.slice(0, this.maxVisible)
      for (const item of visibleItems) {
        const itemEl = this.createItemEl(item)
        this.listEl.appendChild(itemEl)
      }
    }

    this.updateBadge()
  }

  private createItemEl(item: NotificationItem): HTMLDivElement {
    const itemEl = document.createElement('div')
    itemEl.className = 'cmm-notification-center__item'
    itemEl.setAttribute('role', 'listitem')
    itemEl.dataset.notificationId = item.id

    if (item.read !== true) {
      itemEl.classList.add('cmm-notification-center__item--unread')
    }

    const tracked: ItemHandlers = {
      el: itemEl,
      dismissHandler: null,
      actionHandler: null,
      dismissBtn: null,
      actionBtn: null,
    }

    // Icon area
    const level = item.level ?? 'info'
    const iconEl = document.createElement('div')
    iconEl.className = `cmm-notification-center__item-icon cmm-notification-center__item-icon--${level}`
    iconEl.setAttribute('aria-hidden', 'true')
    setSafeIconHtml(iconEl, item.icon ?? LEVEL_ICONS[level])
    itemEl.appendChild(iconEl)

    // Content area
    const contentEl = document.createElement('div')
    contentEl.className = 'cmm-notification-center__item-content'

    const titleEl = document.createElement('div')
    titleEl.className = 'cmm-notification-center__item-title'
    titleEl.textContent = item.title
    contentEl.appendChild(titleEl)

    if (item.description !== undefined) {
      const descEl = document.createElement('div')
      descEl.className = 'cmm-notification-center__item-desc'
      descEl.textContent = item.description
      descEl.title = item.description
      contentEl.appendChild(descEl)
    }

    const timeEl = document.createElement('div')
    timeEl.className = 'cmm-notification-center__item-time'
    timeEl.textContent = formatRelativeTime(item.timestamp)
    contentEl.appendChild(timeEl)

    itemEl.appendChild(contentEl)

    // Actions area
    const actionsEl = document.createElement('div')
    actionsEl.className = 'cmm-notification-center__item-actions'

    if (item.action !== undefined) {
      const actionBtn = document.createElement('button')
      actionBtn.className = 'cmm-notification-center__item-action-btn'
      actionBtn.type = 'button'
      actionBtn.textContent = item.action.label

      const actionOnClick = item.action.onClick
      const actionId = item.id
      const actionHandler = () => {
        actionOnClick(actionId)
      }
      actionBtn.addEventListener('click', actionHandler)
      tracked.actionBtn = actionBtn
      tracked.actionHandler = actionHandler
      actionsEl.appendChild(actionBtn)
    }

    const dismissBtn = document.createElement('button')
    dismissBtn.className = 'cmm-notification-center__item-dismiss'
    dismissBtn.type = 'button'
    dismissBtn.setAttribute('aria-label', 'Dismiss notification')
    dismissBtn.textContent = '\u00d7'

    const dismissId = item.id
    const dismissHandler = () => {
      this.removeNotification(dismissId)
    }
    dismissBtn.addEventListener('click', dismissHandler)
    tracked.dismissBtn = dismissBtn
    tracked.dismissHandler = dismissHandler
    actionsEl.appendChild(dismissBtn)

    itemEl.appendChild(actionsEl)

    this.itemHandlersMap.set(item.id, tracked)

    return itemEl
  }

  private removeItemEl(id: string): void {
    const handlers = this.itemHandlersMap.get(id)
    if (handlers !== undefined) {
      if (handlers.dismissBtn !== null && handlers.dismissHandler !== null) {
        handlers.dismissBtn.removeEventListener('click', handlers.dismissHandler)
      }
      if (handlers.actionBtn !== null && handlers.actionHandler !== null) {
        handlers.actionBtn.removeEventListener('click', handlers.actionHandler)
      }
      handlers.el.remove()
      this.itemHandlersMap.delete(id)
    }
  }

  private cleanupAllItems(): void {
    for (const [id, handlers] of this.itemHandlersMap) {
      if (handlers.dismissBtn !== null && handlers.dismissHandler !== null) {
        handlers.dismissBtn.removeEventListener('click', handlers.dismissHandler)
      }
      if (handlers.actionBtn !== null && handlers.actionHandler !== null) {
        handlers.actionBtn.removeEventListener('click', handlers.actionHandler)
      }
      this.itemHandlersMap.delete(id)
    }
  }

  private updateBadge(): void {
    const count = this.getUnreadCount()
    if (count > 0) {
      this.badgeEl.textContent = String(count)
      this.badgeEl.style.display = ''
      this.badgeEl.setAttribute('aria-label', `${String(count)} unread`)
    } else {
      this.badgeEl.textContent = '0'
      this.badgeEl.style.display = 'none'
    }
  }

  private buildClassName(extra?: string): string {
    const classes = ['cmm-notification-center']
    if (extra) {
      classes.push(extra)
    }
    return classes.join(' ')
  }
}
