import { useEffect, useState, useContext, useMemo } from 'react'
import { Card, Segmented, Flex, Popover, DatePicker, Button, Space } from 'antd'
import {
  ResponsiveContainer,
  BarChart,
  Bar,
  CartesianGrid,
  XAxis,
  YAxis,
  Tooltip,
  Legend
} from 'recharts'

import PropTypes from 'prop-types'
import { getModelByName } from '../../../database/ObjectModels'
import { ApiServerContext } from '../context/ApiServerContext'
import { AuthContext } from '../context/AuthContext'
import dayjs from 'dayjs'
import { useThemeContext } from '../context/ThemeContext'
import MissingPlaceholder from './MissingPlaceholder'
import CheckIcon from '../../Icons/CheckIcon'
import { LoadingOutlined } from '@ant-design/icons'

const ModelHistoryDisplay = ({
  objectType,
  startDate,
  endDate,
  styles,
  height = 400
}) => {
  const { getModelHistory, connected } = useContext(ApiServerContext)
  const { token } = useContext(AuthContext)
  const [historyData, setHistoryData] = useState([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)
  const [timeRange, setTimeRange] = useState('4hrs')
  const [startCustomDate, setStartCustomDate] = useState(null)
  const [endCustomDate, setEndCustomDate] = useState(null)
  const { isDarkMode, getColors } = useThemeContext()
  // Calculate dates based on selected time range or provided props
  const { defaultStartDate, defaultEndDate } = useMemo(() => {
    const now = new Date()

    // If startDate/endDate props are provided, use them (ignore time range)
    if (startDate || endDate) {
      return {
        defaultStartDate:
          startDate || new Date(now.getTime() - 24 * 60 * 60 * 1000),
        defaultEndDate: endDate || now
      }
    }

    // Handle custom date range
    if (timeRange === 'custom' && startCustomDate && endCustomDate) {
      return {
        defaultStartDate: startCustomDate.toDate(),
        defaultEndDate: endCustomDate.toDate()
      }
    }

    // Otherwise, calculate based on selected time range
    const hoursMap = {
      '8hrs': 8,
      '12hrs': 12,
      '24hrs': 24,
      '4hrs': 4,
      '1hrs': 1
    }
    const hours = hoursMap[timeRange] || 1
    return {
      defaultStartDate: new Date(now.getTime() - hours * 60 * 60 * 1000),
      defaultEndDate: now
    }
  }, [startDate, endDate, timeRange, startCustomDate, endCustomDate])

  useEffect(() => {
    if (!objectType || !getModelHistory || !token || !connected) {
      return
    }

    const fetchHistory = async () => {
      try {
        setLoading(true)
        setError(null)
        const fetchedHistory = await getModelHistory(
          objectType,
          defaultStartDate,
          defaultEndDate
        )
        if (fetchedHistory) {
          setHistoryData(fetchedHistory)
        } else {
          setError('Failed to fetch history')
        }
      } catch (err) {
        console.error('Error fetching history:', err)
        setError('Failed to fetch history')
      } finally {
        setLoading(false)
      }
    }

    fetchHistory()
  }, [
    objectType,
    getModelHistory,
    token,
    connected,
    defaultStartDate,
    defaultEndDate
  ])

  if (!objectType) {
    return null
  }

  const model = getModelByName(objectType)

  if (!model) {
    return null
  }

  const modelStats = model.stats || []

  if (modelStats.length === 0) {
    return null
  }

  if (error || !Array.isArray(historyData)) {
    return null
  }

  /**
   * Extracts the stat key from history data based on stat definition name
   * e.g., "states.standby.count" -> "standby", "standbyPrinters" -> "standby"
   * The history data keys are the rollup names (like "standby", "printing")
   */
  const extractStatKey = (statName) => {
    // Handle dot notation paths like "states.standby.count"
    if (statName.includes('.')) {
      const parts = statName.split('.')
      // Extract the middle part (e.g., "standby" from "states.standby.count")
      // Skip first part (usually "states") and last part (usually "count")
      if (parts.length >= 2) {
        return parts[parts.length - 2] // Get second-to-last part
      }
      return parts[parts.length - 1] // Fallback to last part
    }
    // Remove common suffixes
    return statName
      .replace(/Printers?$/i, '')
      .replace(/Jobs?$/i, '')
      .toLowerCase()
  }

  /**
   * Gets a nested value from an object using dot notation
   */
  const getNestedValue = (obj, path) => {
    if (!obj || !path) return undefined
    const keys = path.split('.')
    let value = obj
    for (const key of keys) {
      if (value === null || value === undefined) return undefined
      value = value[key]
    }
    return value
  }

  /**
   * Extracts numeric value from a rollup object
   * Handles both old format (number) and new format ({count: 0}, {sum: 100}, {avg: 5.5})
   */
  const extractValue = (value, preferredOperation) => {
    if (typeof value === 'number') {
      return value
    }
    if (value && typeof value === 'object') {
      // If a preferred operation is specified and exists, use it
      if (preferredOperation && value[preferredOperation] !== undefined) {
        return value[preferredOperation]
      }

      // Handle new format: {count: 0}, {sum: 100}, {avg: 5.5}
      const operationKeys = ['count', 'sum', 'avg']
      for (const key of operationKeys) {
        if (value[key] !== undefined) {
          return value[key]
        }
      }
      // Fallback: return first numeric value found
      const firstValue = Object.values(value)[0]
      return typeof firstValue === 'number' ? firstValue : 0
    }
    return 0
  }

  /**
   * Gets the stat value from history data point
   */
  const getStatValueFromPoint = (point, statDef) => {
    // Handle special combined stats (support both 'combine' and 'sum' properties)
    const combineList = statDef.combine || statDef.sum
    if (combineList) {
      return combineList.reduce((sum, statName) => {
        const tempStatDef = { name: statName }
        const value = getStatValueFromPoint(point, tempStatDef)
        return sum + (value || 0)
      }, 0)
    }

    const statName = statDef.name

    // Determine operation from stat name
    let operation
    if (statName.endsWith('.count')) operation = 'count'
    else if (statName.endsWith('.sum')) operation = 'sum'
    else if (statName.endsWith('.avg')) operation = 'avg'

    let value

    // Extract the key that matches the history data structure
    // History data has keys like "standby", "printing" (the rollup names)
    const statKey = extractStatKey(statName)
    value = point[statKey]

    // If not found with extracted key, try direct access with full path
    if (value === undefined && statName.includes('.')) {
      value = getNestedValue(point, statName)
    }

    // If still not found, try with original stat name
    if (value === undefined) {
      value = point[statName]
    }

    return extractValue(value, operation)
  }

  // Transform data for the chart using model stats configuration
  // Each data point has: { date: '2024-01-01', standby: {count: 0}, printing: {count: 0}, ... }
  // We need to transform to: [{ date: '2024-01-01', category: 'Ready', value: 0 }, ...]
  const chartData = historyData.flatMap((point) => {
    return modelStats.map((statDef) => {
      const statValue = getStatValueFromPoint(point, statDef)
      const label = statDef.label || statDef.name
      return {
        date: point.date,
        dateFormatted: dayjs(point.date).format('DD/MM HH:mm'),
        category: label,
        value: statValue || 0
      }
    })
  })

  // Sort by date
  chartData.sort((a, b) => {
    return new Date(a.date) - new Date(b.date)
  })

  const themeColors = getColors()

  // Create color mapping from model stats
  const colors = {
    success: themeColors.colorSuccess,
    processing: themeColors.colorInfo,
    error: themeColors.colorError,
    warning: themeColors.colorWarning,
    default: '#8c8c8c',
    cyan: themeColors.colorCyan,
    pink: themeColors.colorPink,
    purple: themeColors.colorPurple,
    magenta: themeColors.colorMagenta,
    volcano: themeColors.colorVolcano
  }

  // Build color range array based on model stats order
  const colorRange = modelStats.map((stat) => {
    if (stat.color) {
      return colors[stat.color] || stat.color
    }
    return colors.default
  })

  const chartRows = Array.from(
    chartData
      .reduce((acc, item) => {
        const existing = acc.get(item.date) || {
          date: item.date,
          dateFormatted: item.dateFormatted
        }
        existing[item.category] = item.value
        acc.set(item.date, existing)
        return acc
      }, new Map())
      .values()
  ).sort((a, b) => new Date(a.date) - new Date(b.date))

  const customTimeRangeContent = (
    <Space.Compact>
      <DatePicker.RangePicker
        onChange={(dates) => {
          if (dates) {
            setStartCustomDate(dates[0])
            setEndCustomDate(dates[1])
          } else {
            setStartCustomDate(null)
            setEndCustomDate(null)
          }
        }}
        value={[startCustomDate, endCustomDate]}
      />
      <Button
        type='primary'
        onClick={() => {
          if (startCustomDate && endCustomDate) {
            setTimeRange('custom')
          }
        }}
        disabled={!startCustomDate || !endCustomDate}
        icon={<CheckIcon />}
      ></Button>
    </Space.Compact>
  )

  return (
    <Card
      style={{ width: '100%' }}
      styles={{ body: { padding: '12px', ...styles } }}
    >
      <Flex gap='small' vertical>
        {!startDate && !endDate && (
          <Flex justify='space-between'>
            <Flex align='center' gap='5px'>
              <Popover
                content={customTimeRangeContent}
                trigger='hover'
                arrow={false}
                placement='bottomLeft'
                styles={{ body: { borderRadius: '22.5px' } }}
              >
                <Segmented
                  size='small'
                  options={[{ label: 'Custom', value: 'custom' }]}
                  value={timeRange}
                  onChange={setTimeRange}
                  disabled={loading}
                />
              </Popover>
            </Flex>

            <Flex gap='middle'>
              {loading == true && <LoadingOutlined />}
              <Segmented
                size='small'
                options={[
                  { label: '24hr', value: '24hrs' },
                  { label: '12hr', value: '12hrs' },
                  { label: '8hr', value: '8hrs' },
                  { label: '4hr', value: '4hrs' },
                  { label: '1hr', value: '1hrs' }
                ]}
                value={timeRange}
                onChange={setTimeRange}
                disabled={loading}
              />
            </Flex>
          </Flex>
        )}
        {chartRows.length > 0 && (
          <div style={{ width: '100%', height: `${height}px` }}>
            <ResponsiveContainer width='100%' height='100%'>
              <BarChart data={chartRows}>
                <CartesianGrid
                  strokeDasharray='3 3'
                  stroke={isDarkMode ? '#303030' : '#f0f0f0'}
                />
                <XAxis
                  dataKey='dateFormatted'
                  tick={{
                    fill: isDarkMode ? '#d9d9d9' : '#595959',
                    fontSize: 12
                  }}
                />
                <YAxis
                  tick={{
                    fill: isDarkMode ? '#d9d9d9' : '#595959',
                    fontSize: 12
                  }}
                />
                <Tooltip
                  contentStyle={{
                    backgroundColor: isDarkMode ? '#1f1f1f' : '#ffffff',
                    border: `1px solid ${isDarkMode ? '#303030' : '#f0f0f0'}`,
                    borderRadius: 8
                  }}
                />
                <Legend />
                {modelStats.map((statDef, index) => {
                  const category = statDef.label || statDef.name
                  return (
                    <Bar
                      key={category}
                      dataKey={category}
                      stackId='history'
                      fill={colorRange[index] || colors.default}
                    />
                  )
                })}
              </BarChart>
            </ResponsiveContainer>
          </div>
        )}
        {loading == false && chartRows.length == 0 && (
          <Flex
            justify='center'
            align='center'
            style={{ height: `${height}px` }}
          >
            <MissingPlaceholder message='No data available.' />
          </Flex>
        )}
      </Flex>
    </Card>
  )
}

ModelHistoryDisplay.propTypes = {
  objectType: PropTypes.string.isRequired,
  startDate: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.instanceOf(Date)
  ]),
  styles: PropTypes.object,
  endDate: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
  height: PropTypes.oneOfType([PropTypes.number, PropTypes.string])
}

export default ModelHistoryDisplay
