/* Modified DateRangePicker based DateRangePicker by John Polacek (https://github.com/johnpolacek/date-range-picker-for-shadcn) (MIT License) */

import { CalendarIcon, Cross2Icon } from '@radix-ui/react-icons'

import React, { useEffect, useState } from 'react'

import { Button } from '@components/ui/button/button'
import { Calendar } from '@components/ui/calendar/calendar'
import { Popover, PopoverContent, PopoverTrigger } from '@components/ui/popover/popover'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@components/ui/select/select'
import { cn } from '@lib/utils'

export type GanttTimerangePickerProps = {
  initialDateFrom?: Date
  initialDateTo?: Date
  // onRangeChange now receives ISO string date range (UTC-normalized) or undefined
  onRangeChange?: (values: ISODateRange | undefined) => void
  timelineStart?: Date
  timelineEnd?: Date
}

type DateRange = {
  from?: Date
  to?: Date
}

// ISODateRange with ISO string dates for UTC-normalized range for querying
export type ISODateRange = {
  from?: string
  to?: string
}

// reusable helper: convert an ISO UTC-normalized date string back to a local Date
export const isoToLocalDate = (iso?: string): Date | undefined => {
  if (!iso) {
    return undefined
  }
  const d = new Date(iso) // parse ISO instant
  // build a local Date that has the same calendar day as the UTC date parts
  return new Date(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate())
}

// helper to convert internal Date range to ISO strings normalized to UTC start/end of day
const convertRangeToISO = (r?: { from?: Date; to?: Date } | undefined): ISODateRange | undefined => {
  if (!(r?.from && r?.to)) {
    return undefined
  }
  const from = r.from
  const to = r.to

  const fromIso = new Date(
    Date.UTC(from.getUTCFullYear(), from.getUTCMonth(), from.getUTCDate(), 0, 0, 0, 0),
  ).toISOString()
  const toIso = new Date(
    Date.UTC(to.getUTCFullYear(), to.getUTCMonth(), to.getUTCDate(), 23, 59, 59, 999),
  ).toISOString()

  return { from: fromIso, to: toIso }
}

type Preset = {
  name: string
  label: string
}

const PRESETS: Preset[] = [
  { name: 'sinceStart', label: 'Since timeline start' },
  { name: 'thisYear', label: 'This year' },
  { name: 'nextFullYear', label: 'Next full year' },
  { name: 'next1Year', label: 'Next 1 year' },
  { name: 'next2Year', label: 'Next 2 years' },
  { name: 'next3Year', label: 'Next 3 years' },
  { name: 'untilEnd', label: 'Until timeline end' },
]

export const GanttTimerangePicker = ({
  initialDateFrom,
  initialDateTo,
  onRangeChange,
  timelineStart,
  timelineEnd,
}: GanttTimerangePickerProps) => {
  const [range, setRange] = useState<DateRange | undefined>(
    initialDateFrom || initialDateTo ? { from: initialDateFrom, to: initialDateTo } : undefined,
  )
  const [isOpen, setIsOpen] = useState(false)
  const [selectedPreset, setSelectedPreset] = useState<string | undefined>(undefined)
  const [isSmallScreen, setIsSmallScreen] = useState(
    globalThis.window === undefined ? false : globalThis.window.innerWidth < 960,
  )

  // listen to window resize events to enable a small screen mode
  useEffect(() => {
    const handleResize = (): void => {
      setIsSmallScreen(globalThis.window.innerWidth < 960)
    }
    globalThis.window.addEventListener('resize', handleResize)
    // Clean up event listener on unmount
    return () => {
      globalThis.window.removeEventListener('resize', handleResize)
    }
  }, [])

  const getPresetRange = (presetName: string, timelineStart?: Date, timelineEnd?: Date): DateRange => {
    const preset = PRESETS.find(({ name }) => name === presetName)
    if (!preset) {
      throw new Error(`Unknown date range preset: ${presetName}`)
    }
    const from = new Date()
    const to = new Date()

    switch (preset.name) {
      case 'sinceStart':
        if (timelineStart) {
          from.setFullYear(timelineStart.getFullYear(), timelineStart.getMonth(), timelineStart.getDate())
        }
        from.setHours(0, 0, 0, 0)
        to.setHours(23, 59, 59, 999)
        break
      case 'thisYear':
        from.setFullYear(from.getFullYear(), 0, 1)
        from.setHours(0, 0, 0, 0)
        to.setFullYear(to.getFullYear(), 11, 31)
        to.setHours(23, 59, 59, 999)
        break
      case 'nextFullYear':
        from.setFullYear(from.getFullYear() + 1, 0, 1)
        from.setHours(0, 0, 0, 0)
        to.setFullYear(to.getFullYear() + 1, 11, 31)
        to.setHours(23, 59, 59, 999)
        break
      case 'next1Year':
        from.setHours(0, 0, 0, 0)
        to.setFullYear(to.getFullYear() + 1)
        to.setHours(23, 59, 59, 999)
        break
      case 'next2Year':
        from.setHours(0, 0, 0, 0)
        to.setFullYear(to.getFullYear() + 2)
        to.setHours(23, 59, 59, 999)
        break
      case 'next3Year':
        from.setHours(0, 0, 0, 0)
        to.setFullYear(to.getFullYear() + 3)
        to.setHours(23, 59, 59, 999)
        break
      case 'untilEnd':
        from.setHours(0, 0, 0, 0)
        if (timelineEnd) {
          to.setFullYear(timelineEnd.getFullYear(), timelineEnd.getMonth(), timelineEnd.getDate())
        }
        to.setHours(23, 59, 59, 999)
        break
    }

    return { from, to }
  }

  // automatically detect selected preset when range changes (internal Date range)
  useEffect(() => {
    if (!range?.from || !range.to) {
      setSelectedPreset(undefined)
      return
    }
    for (const preset of PRESETS) {
      const presetRange = getPresetRange(preset.name, timelineStart, timelineEnd)
      if (!presetRange.from || !presetRange.to) {
        continue
      }

      const normalizedRangeFrom = new Date(range.from)
      normalizedRangeFrom.setHours(0, 0, 0, 0)
      const normalizedPresetFrom = new Date(presetRange.from)
      normalizedPresetFrom.setHours(0, 0, 0, 0)

      const normalizedRangeTo = new Date(range.to)
      normalizedRangeTo.setHours(0, 0, 0, 0)
      const normalizedPresetTo = new Date(presetRange.to)
      normalizedPresetTo.setHours(0, 0, 0, 0)

      if (
        normalizedRangeFrom.getTime() === normalizedPresetFrom.getTime() &&
        normalizedRangeTo.getTime() === normalizedPresetTo.getTime()
      ) {
        setSelectedPreset(preset.name)
        return
      }
    }
    setSelectedPreset(undefined)
  }, [range, timelineStart, timelineEnd])

  return (
    <Popover
      modal={true}
      open={isOpen}
      onOpenChange={(isOpen) => {
        setIsOpen(isOpen)
        // on close, emit ISO-normalized range
        if (!isOpen) {
          onRangeChange?.(convertRangeToISO(range))
        }
      }}
    >
      <PopoverTrigger asChild>
        <Button size="iconSm" variant="extraGhost2">
          <CalendarIcon className="size-3" />
        </Button>
      </PopoverTrigger>
      <PopoverContent align="start" className="w-auto p-2 m-2 relative">
        <div className="flex justify-between items-center">
          <span className="pl-2">
            {range?.from && range?.to
              ? `${range.from.toLocaleDateString()} - ${range.to.toLocaleDateString()} selected`
              : 'No range selected'}
          </span>
          <Button
            variant="outline"
            size="icon"
            className="size-6 rounded"
            onClick={() => {
              setIsOpen(false)
            }}
          >
            <Cross2Icon className="size-5" />
          </Button>
        </div>
        <div className="flex">
          <div className="flex">
            <div className="flex flex-col">
              {isSmallScreen && (
                <Select
                  defaultValue={selectedPreset}
                  onValueChange={(value) => {
                    setRange(getPresetRange(value))
                  }}
                >
                  <SelectTrigger size="xs" className="w-[180px] mx-auto mt-2">
                    <SelectValue placeholder="Select..." />
                  </SelectTrigger>
                  <SelectContent>
                    {PRESETS.map((preset) => (
                      <SelectItem key={preset.name} value={preset.name}>
                        {preset.label}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              )}
              <div className={cn('h-[325px]', isSmallScreen && 'mb-8')}>
                <Calendar
                  mode="range"
                  showYearNavigation={true}
                  disabled={(date) =>
                    (timelineStart ? date < timelineStart : false) || (timelineEnd ? date > timelineEnd : false)
                  }
                  onSelect={(value: { from?: Date; to?: Date } | undefined) => {
                    if (value?.from != null) {
                      const from = value.from
                      const to = value.to ?? value.from
                      // Normalize the range so that from <= to
                      setRange(to < from ? { from: to, to: from } : { from, to })
                    }
                  }}
                  selected={range?.from && range?.to ? { from: range.from, to: range.to } : undefined}
                  numberOfMonths={isSmallScreen ? 1 : 2}
                  defaultMonth={new Date(new Date().setMonth(new Date().getMonth() - (isSmallScreen ? 0 : 1)))}
                />
              </div>
            </div>
          </div>
          {!isSmallScreen && (
            <div className="flex flex-col items-start gap-1 pr-2 pl-12 py-3">
              {PRESETS.map((preset) => (
                <PresetButton
                  key={preset.name}
                  preset={preset.name}
                  label={preset.label}
                  isSelected={selectedPreset === preset.name}
                  onSelect={(presetName) => setRange(getPresetRange(presetName))}
                />
              ))}
            </div>
          )}
        </div>
        <div className="flex gap-2 absolute bottom-3 right-3">
          <Button
            variant="outlineInvert"
            size="smLow"
            className=""
            onClick={() => {
              setRange(undefined)
            }}
          >
            Clear Range
          </Button>
          <Button
            size="smLow"
            onClick={() => {
              setIsOpen(false)
              onRangeChange?.(convertRangeToISO(range))
            }}
          >
            Apply
          </Button>
        </div>
      </PopoverContent>
    </Popover>
  )
}

const PresetButton = ({
  preset,
  label,
  isSelected,
  onSelect,
}: {
  preset: string
  label: string
  isSelected: boolean
  onSelect: (preset: string) => void
}): React.ReactNode => (
  <Button
    className={`w-full justify-start ${isSelected ? 'pointer-events-none bg-accent' : ''}`}
    variant="ghost"
    size="smLow"
    onClick={() => onSelect(preset)}
  >
    {label}
  </Button>
)
