import type { Table } from '@tanstack/react-table'

import { useCallback, useEffect, useMemo, useRef } from 'react'
import { toast } from 'sonner'

import { type TableViewState, useTableViewConfig } from '@hooks/use-table-view-config'
import { DataGridViewMenu } from '@postxl/ui-components'

export type TableViewPanelProps<TData> = {
  /** The model/table name to scope views to (e.g. 'user', 'publication'). */
  model: string
  /** The TanStack Table instance (from useDataGrid). */
  table: Table<TData>
  /** The current user's ID. */
  currentUserId?: string
  /** Whether the current user has admin privileges. */
  isAdmin?: boolean
  /** Current filter state — included when saving a view. */
  filters?: Record<string, unknown>
  /** Current sort state — included when saving a view. */
  sort?: { field: string; direction: 'asc' | 'desc' }[]
  /** Called when a view is applied and its filters should be restored. */
  onFiltersChange?: (filters: Record<string, unknown>) => void
  /** Called when a view is applied and its sort should be restored. */
  onSortChange?: (sort: { field: string; direction: 'asc' | 'desc' }[]) => void
  /** Called when the active view is cleared, so the parent can restore default table state. */
  onClearView?: () => void
}

/**
 * Connects the table view config hook to the DataGridViewMenu component.
 *
 * Handles:
 * - Collecting current table state (filters, sort, column visibility/order/pinning)
 * - Saving/updating views with automatic JSON serialization
 * - Applying loaded views back to the table and page filter/sort state
 * - Auto-restoring the previously active view on mount
 * - Rename and delete operations
 * - Deselecting the active view when the user changes any tracked state
 */
export function TableViewPanel<TData>({
  model,
  table,
  currentUserId,
  isAdmin,
  filters,
  sort,
  onFiltersChange,
  onSortChange,
  onClearView,
}: TableViewPanelProps<TData>) {
  const viewConfig = useTableViewConfig({ model, currentUserId })
  const hasAutoAppliedRef = useRef(false)
  // 'pending' = view just applied, waiting for state to settle before capturing baseline
  const appliedStateRef = useRef<string | null>(null)

  // Track current state of all view-stored properties for change detection
  const { columnVisibility, columnOrder, columnPinning, columnSizing } = table.getState()
  const currentStateKey = useMemo(
    () => JSON.stringify({ filters, sort, columnVisibility, columnOrder, columnPinning, columnSizing }),
    [filters, sort, columnVisibility, columnOrder, columnPinning, columnSizing],
  )

  const applyViewState = useCallback(
    (viewId: string) => {
      const view = viewConfig.views.find((v) => v.id === viewId)
      if (!view) {
        return
      }
      const state = viewConfig.parseViewState(view)
      appliedStateRef.current = 'pending'

      if (onFiltersChange) {
        onFiltersChange(state.filters ?? {})
      }
      if (onSortChange) {
        onSortChange(state.sorting ?? [])
      }
      if (state.columnVisibility) {
        table.setColumnVisibility(state.columnVisibility)
      }
      if (state.columnOrder) {
        table.setColumnOrder(state.columnOrder)
      }
      if (state.columnPinning) {
        table.setColumnPinning(state.columnPinning)
      }
      if (state.columnSizing) {
        table.setColumnSizing(state.columnSizing)
      }
    },
    [viewConfig, onFiltersChange, onSortChange, table],
  )

  // Auto-apply persisted view once views have loaded
  useEffect(() => {
    if (!viewConfig.isLoaded || hasAutoAppliedRef.current) {
      return
    }
    hasAutoAppliedRef.current = true

    if (viewConfig.activeViewId && viewConfig.views.find((v) => v.id === viewConfig.activeViewId)) {
      applyViewState(viewConfig.activeViewId)
    }
  }, [viewConfig.isLoaded, viewConfig.activeViewId, viewConfig.views, applyViewState])

  // Deselect active view when any stored state property drifts from the applied view.
  // Debounced: each state change resets the timer, so transient changes from table
  // auto-sizing or batched React updates don't trigger false positives.
  const { activeViewId, clearActiveView } = viewConfig
  useEffect(() => {
    if (!activeViewId || !appliedStateRef.current) {
      return
    }

    const timer = setTimeout(() => {
      if (appliedStateRef.current === 'pending') {
        appliedStateRef.current = currentStateKey
      } else if (currentStateKey !== appliedStateRef.current) {
        clearActiveView()
        appliedStateRef.current = null
      }
    }, 300)
    return () => clearTimeout(timer)
  }, [currentStateKey, activeViewId, clearActiveView])

  const collectTableState = useCallback(
    (): TableViewState => ({
      filters: filters,
      sorting: sort,
      columnVisibility: table.getState().columnVisibility,
      columnOrder: table.getState().columnOrder,
      columnPinning: table.getState().columnPinning as { left?: string[]; right?: string[] },
      columnSizing: table.getState().columnSizing,
    }),
    [filters, sort, table],
  )

  const handleSaveView = useCallback(
    ({ name, isGlobal }: { name: string; isGlobal: boolean }) => {
      // Snapshot current state — saving makes the current state match the view
      appliedStateRef.current = currentStateKey
      void viewConfig.saveView({ name, isGlobal, state: collectTableState() }).then((view) => {
        toast.success(`View "${view.name}" saved`)
      })
    },
    [viewConfig, collectTableState, currentStateKey],
  )

  const handleUpdateView = useCallback(
    (viewId: string) => {
      // Snapshot current state — updating makes the current state match the view
      appliedStateRef.current = currentStateKey
      void viewConfig.updateView(viewId, collectTableState()).then(() => {
        viewConfig.setActiveViewId(viewId)
        toast.success('View updated')
      })
    },
    [viewConfig, collectTableState, currentStateKey],
  )

  const handleApplyView = useCallback(
    (viewId: string) => {
      applyViewState(viewId)
      viewConfig.setActiveViewId(viewId)
    },
    [applyViewState, viewConfig],
  )

  const handleRenameView = useCallback(
    (viewId: string, name: string) => {
      void viewConfig.renameView(viewId, name).then(() => {
        toast.success(`View renamed to "${name}"`)
      })
    },
    [viewConfig],
  )

  const handleClearView = useCallback(() => {
    appliedStateRef.current = null
    viewConfig.clearActiveView()
    onClearView?.()
    if (onFiltersChange) {
      onFiltersChange({})
    }
    if (onSortChange) {
      onSortChange([])
    }
  }, [viewConfig, onClearView, onFiltersChange, onSortChange])

  const handleDeleteView = useCallback(
    (viewId: string) => {
      void viewConfig.deleteView(viewId).then(() => {
        toast.success('View deleted')
      })
    },
    [viewConfig],
  )

  return (
    <DataGridViewMenu
      table={table}
      savedViews={viewConfig.savedViews}
      activeViewId={viewConfig.activeViewId}
      currentUserId={currentUserId}
      isAdmin={isAdmin}
      onApplyView={handleApplyView}
      onSaveView={handleSaveView}
      onUpdateView={handleUpdateView}
      onRenameView={handleRenameView}
      onDeleteView={handleDeleteView}
      onClearView={handleClearView}
    />
  )
}
