import React, { useState, useEffect } from 'react'
import type { Process, Server } from '../api/baserow'

const SERVER_ICON = (
  <svg width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor">
    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01" />
  </svg>
)

type ViewMode = '2H' | '6H' | 'Arbeitszeit' | 'Nachts' | 'Tag' | 'Woche' | 'Monat'

function getViewBounds(mode: ViewMode, referenceDate: Date): { start: Date, end: Date, spanHours: number } {
  const d = new Date(referenceDate)
  let start = new Date(d)
  let end = new Date(d)
  let spanHours = 0

  if (mode === '2H') {
    start = new Date(d.getTime() - 1 * 3600_000)
    end = new Date(d.getTime() + 1 * 3600_000)
    spanHours = 2
  } else if (mode === '6H') {
    start = new Date(d.getTime() - 3 * 3600_000)
    end = new Date(d.getTime() + 3 * 3600_000)
    spanHours = 6
  } else if (mode === 'Arbeitszeit') {
    start.setHours(6, 0, 0, 0)
    end.setHours(18, 0, 0, 0)
    spanHours = 12
  } else if (mode === 'Nachts') {
    if (d.getHours() < 6) d.setDate(d.getDate() - 1)
    start = new Date(d)
    start.setHours(18, 0, 0, 0)
    end = new Date(start.getTime() + 12 * 3600_000)
    spanHours = 12
  } else if (mode === 'Tag') {
    start.setHours(0, 0, 0, 0)
    end = new Date(start.getTime() + 24 * 3600_000)
    spanHours = 24
  } else if (mode === 'Woche') {
    const day = start.getDay()
    const diff = start.getDate() - day + (day === 0 ? -6 : 1)
    start.setDate(diff)
    start.setHours(0, 0, 0, 0)
    end = new Date(start.getTime() + 7 * 24 * 3600_000)
    spanHours = 168
  } else if (mode === 'Monat') {
    start.setDate(1)
    start.setHours(0, 0, 0, 0)
    end = new Date(start.getTime())
    end.setMonth(end.getMonth() + 1)
    spanHours = (end.getTime() - start.getTime()) / 3600_000
  }

  return { start, end, spanHours }
}

function parseTime(tStr: string | null | undefined): [number, number, number] {
  if (!tStr) return [12, 0, 0]
  const [h, m, s] = tStr.split(':').map(Number)
  return [isNaN(h) ? 12 : h, isNaN(m) ? 0 : m, isNaN(s) ? 0 : s]
}

export default function GanttChart({ processes, servers, onRunningChange }: { processes: Process[], servers?: Server[], onRunningChange?: (count: number) => void }) {
  const [view, setView] = useState<ViewMode>('Tag')
  const [now, setNow] = useState(new Date())

  useEffect(() => {
    const interval = setInterval(() => setNow(new Date()), 60_000)
    return () => clearInterval(interval)
  }, [])

  // Calculate Running Processes whenever now or processes change
  useEffect(() => {
    if (!onRunningChange) return

    let count = 0
    const nowMs = now.getTime()

    for (const proc of processes) {
      if (proc.Status?.value !== 'aktiv') continue

      // Minimal version of renderBlocks logic to find if any block overlaps 'now'
      const rep = proc.Wiederholung?.value
      let isRunning = false

      // We only need to check today's blocks for 'now'
      const iter = new Date(now)
      iter.setHours(0, 0, 0, 0)

      const check = (stMs: number, durSecs: number) => {
        const edMs = stMs + (durSecs * 1000)
        return nowMs >= stMs && nowMs <= edMs
      }

      const dur = parseInt(proc.Dauer || '0') || 60
      const dayStrings = ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa']
      const currentDayStr = dayStrings[iter.getDay()]
      
      // Weekday filter
      if (proc.Wochentage && proc.Wochentage.trim() !== '') {
        if (!proc.Wochentage.includes(currentDayStr) && rep !== 'WOCHENENDE' && rep !== 'WERKTAGE') {
          continue 
        }
      }

      if (rep === 'TAEGLICH') {
        const [h, m, s] = parseTime(proc.Start)
        const d = new Date(iter)
        d.setHours(h, m, s)
        if (check(d.getTime(), dur)) isRunning = true
      } else if (rep === 'STUENDLICH') {
        const d = new Date(iter)
        d.setHours(now.getHours(), proc['Start Minute'] || 0, 0)
        if (check(d.getTime(), dur)) isRunning = true
      } else if (rep === 'WOECHENTLICH') {
        const [h, m, s] = parseTime(proc.Start)
        const d = new Date(iter)
        d.setHours(h, m, s)
        if (check(d.getTime(), dur)) isRunning = true
      } else if (rep === 'WERKTAGE') {
        const day = iter.getDay()
        if (day >= 1 && day <= 5) {
          const [h, m, s] = parseTime(proc.Start)
          const d = new Date(iter)
          d.setHours(h, m, s)
          if (check(d.getTime(), dur)) isRunning = true
        }
      } else if (rep === 'WOCHENENDE') {
        const day = iter.getDay()
        if (day === 0 || day === 6) {
          const [h, m, s] = parseTime(proc.Start)
          const d = new Date(iter)
          d.setHours(h, m, s)
          if (check(d.getTime(), dur)) isRunning = true
        }
      } else if (rep === 'INTERVALL') {
        const [fh, fm, fs] = parseTime(proc['Erste Ausführung'])
        const [wh, wm, ws] = parseTime(proc['Ausführung bis'] || '23:59:59')
        const interval = Math.max(1, parseInt(proc['Intervall zwischen'] || '0') || 60)
        
        const dayStart = new Date(iter)
        dayStart.setHours(fh, fm, fs)
        const dayEnd = new Date(iter)
        dayEnd.setHours(wh, wm, ws)
        
        // Find if any interval in this day contains 'now'
        if (nowMs >= dayStart.getTime() && nowMs <= dayEnd.getTime()) {
          const diffSeconds = (nowMs - dayStart.getTime()) / 1000
          const remainder = diffSeconds % interval
          if (remainder <= dur) isRunning = true
        }
      } else if (rep === 'MONATLICH_FESTER_TAG') {
        if (iter.getDate() === 1) {
          const [h, m, s] = parseTime(proc.Start)
          const d = new Date(iter)
          d.setHours(h, m, s)
          if (check(d.getTime(), dur)) isRunning = true
        }
      }

      if (isRunning) count++
    }

    onRunningChange(count)
  }, [now, processes, onRunningChange])

  const { start: viewStart, end: viewEnd, spanHours } = getViewBounds(view, now)
  const spanMs = viewEnd.getTime() - viewStart.getTime()

  // Generate Hour Markers
  const markers = []
  let mkIter = new Date(viewStart)
  
  // Dynamic Snapping
  if (view === '2H') {
    mkIter.setMinutes(Math.floor(mkIter.getMinutes() / 15) * 15, 0, 0)
  } else if (view === '6H') {
    mkIter.setMinutes(Math.floor(mkIter.getMinutes() / 30) * 30, 0, 0)
  } else if (view === 'Monat') {
    mkIter.setHours(0, 0, 0, 0)
  } else if (view === 'Woche') {
    mkIter.setHours(Math.floor(mkIter.getHours() / 6) * 6, 0, 0, 0)
  } else {
    mkIter.setMinutes(0, 0, 0)
  }

  while (mkIter <= viewEnd) {
    const pct = ((mkIter.getTime() - viewStart.getTime()) / spanMs) * 100
    let label = ''

    if (view === 'Monat') {
      label = mkIter.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' })
    } else if (view === 'Woche') {
       if (mkIter.getHours() === 0) {
         label = mkIter.toLocaleDateString('de-DE', { weekday: 'short', day: '2-digit', month: '2-digit' })
       } else {
         label = mkIter.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })
       }
    } else {
      label = mkIter.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })
    }

    if (pct >= 0 && pct <= 100) {
      markers.push({ key: mkIter.getTime(), pct, label })
    }
    
    // Dynamic Increment
    if (view === '2H') mkIter.setMinutes(mkIter.getMinutes() + 15)
    else if (view === '6H') mkIter.setMinutes(mkIter.getMinutes() + 30)
    else if (view === 'Woche') mkIter.setHours(mkIter.getHours() + 6)
    else if (view === 'Monat') mkIter.setDate(mkIter.getDate() + 1)
    else mkIter.setHours(mkIter.getHours() + 1)
  }

  // Now line
  const nowPct = ((now.getTime() - viewStart.getTime()) / spanMs) * 100
  const isNowVisible = nowPct >= 0 && nowPct <= 100

  // Group processes by server
  const serverGroups = new Map<string, Process[]>()
  for (const p of processes) {
    // Only render active processes
    if (p.Status?.value && p.Status.value !== 'aktiv') continue

    // APPLY VISIBILITY MAX VIEW FILTER (CRITICAL MEMORY PRESERVATION)
    const maxView = p.Sichtbarkeit?.value || 'Immer'
    if (maxView === 'Max 6 Stunden') {
      if (view === 'Tag' || view === 'Woche' || view === 'Monat' || view === 'Arbeitszeit' || view === 'Nachts') continue
    } else if (maxView === 'Max 1 Tag') {
      if (view === 'Woche' || view === 'Monat') continue
    }

    const sName = p.Server?.[0]?.value || 'Ohne Server'
    if (!serverGroups.has(sName)) serverGroups.set(sName, [])
    serverGroups.get(sName)!.push(p)
  }

  const renderBlocksForProcess = (proc: Process) => {
    const rawBlocks: { stMs: number; edMs: number }[] = []
    const rep = proc.Wiederholung?.value

    let iter = new Date(viewStart)
    iter.setHours(0, 0, 0, 0)
    // Add one day padding
    iter.setDate(iter.getDate() - 1)
    const iterEnd = new Date(viewEnd)
    iterEnd.setDate(iterEnd.getDate() + 1)

    const pushRawBlock = (startTimeStr: Date, durationSecs: number) => {
      const stMs = startTimeStr.getTime()
      const edMs = stMs + (durationSecs * 1000)
      if (edMs < viewStart.getTime() || stMs > viewEnd.getTime()) return
      rawBlocks.push({ stMs, edMs })
    }

    while (iter < iterEnd) {
      // Apply weekday filter globally
      const dayStrings = ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa']
      const currentDayStr = dayStrings[iter.getDay()]
      if (proc.Wochentage && proc.Wochentage.trim() !== '') {
        if (!proc.Wochentage.includes(currentDayStr) && rep !== 'WOCHENENDE' && rep !== 'WERKTAGE') {
          iter.setDate(iter.getDate() + 1)
          continue
        }
      }

      if (rep === 'TAEGLICH') {
        const [h, m, s] = parseTime(proc.Start)
        const d = new Date(iter)
        d.setHours(h, m, s)
        pushRawBlock(d, parseInt(proc.Dauer || '0') || 60)
      } else if (rep === 'STUENDLICH') {
        for (let hh = 0; hh < 24; hh++) {
          const d = new Date(iter)
          d.setHours(hh, proc['Start Minute'] || 0, 0)
          pushRawBlock(d, Math.max(1, parseInt(proc.Dauer || '0') || 60))
        }
      } else if (rep === 'WOECHENTLICH') {
        const [h, m, s] = parseTime(proc.Start)
        const d = new Date(iter)
        d.setHours(h, m, s)
        pushRawBlock(d, parseInt(proc.Dauer || '0') || 60)
      } else if (rep === 'WERKTAGE') {
        const day = iter.getDay()
        if (day >= 1 && day <= 5) {
          const [h, m, s] = parseTime(proc.Start)
          const d = new Date(iter)
          d.setHours(h, m, s)
          pushRawBlock(d, parseInt(proc.Dauer || '0') || 60)
        }
      } else if (rep === 'WOCHENENDE') {
        const day = iter.getDay()
        if (day === 0 || day === 6) {
          const [h, m, s] = parseTime(proc.Start)
          const d = new Date(iter)
          d.setHours(h, m, s)
          pushRawBlock(d, parseInt(proc.Dauer || '0') || 60)
        }
      } else if (rep === 'INTERVALL') {
        const [fh, fm, fs] = parseTime(proc['Erste Ausführung'])
        const [wh, wm, ws] = parseTime(proc['Ausführung bis'] || '23:59:59')
        const interval = Math.max(1, parseInt(proc['Intervall zwischen'] || '0') || 60)
        const dur = parseInt(proc.Dauer || '0') || 60
        
        const dayStart = new Date(iter)
        dayStart.setHours(fh, fm, fs)
        const dayEnd = new Date(iter)
        dayEnd.setHours(wh, wm, ws)
        
        for (let t = dayStart.getTime(); t <= dayEnd.getTime(); t += interval * 1000) {
          pushRawBlock(new Date(t), dur)
        }
      } else if (rep === 'MONATLICH_FESTER_TAG') {
        if (iter.getDate() === 1) {
          const [h, m, s] = parseTime(proc.Start)
          const d = new Date(iter)
          d.setHours(h, m, s)
          pushRawBlock(d, parseInt(proc.Dauer || '0') || 60)
        }
      } else {
        const [h, m, s] = parseTime(proc.Start || '12:00:00')
        const d = new Date(iter)
        d.setHours(h, m, s)
        pushRawBlock(d, parseInt(proc.Dauer || '0') || 30)
      }
      iter.setDate(iter.getDate() + 1)
    }

    // Sort blocks by start time
    rawBlocks.sort((a, b) => a.stMs - b.stMs)

    // Merge blocks that are overlapping or too close to render individually (e.g. gap < 0.2% of screen)
    const mergedBlocks: { stMs: number; edMs: number }[] = []
    // 0.2% of total span ms is threshold for merging
    const mergeThreshold = spanMs * 0.002

    for (const b of rawBlocks) {
      if (mergedBlocks.length === 0) {
        mergedBlocks.push({ ...b })
      } else {
        const prev = mergedBlocks[mergedBlocks.length - 1]
        // If start is before or overlaps with prev.end + visual threshold
        if (b.stMs <= prev.edMs + mergeThreshold) {
          prev.edMs = Math.max(prev.edMs, b.edMs)
        } else {
          mergedBlocks.push({ ...b })
        }
      }
    }

    // Now map them to standard JSX layout
    return mergedBlocks.map(b => {
      const left = Math.max(0, ((b.stMs - viewStart.getTime()) / spanMs) * 100)
      const rightBounds = Math.min(viewEnd.getTime(), b.edMs)
      let width = ((rightBounds - Math.max(viewStart.getTime(), b.stMs)) / spanMs) * 100
      
      if (width < 0.2) width = 0.2

      return (
        <div 
          key={`${b.stMs}-${b.edMs}`} 
          className="gantt-block"
          style={{ left: `${left}%`, width: `${width}%`, backgroundColor: proc.Farbe || 'var(--blue)' }}
          title={`${proc.Name}\nZusammengefasste Laufzeit: ${new Date(b.stMs).toLocaleTimeString('de-DE')} bis ${new Date(b.edMs).toLocaleTimeString('de-DE')}`}
        >
          {width > 5 && <span className="gantt-block-label">{proc.Name}</span>}
        </div>
      )
    })
  }

  return (
    <div className="gantt-container" style={{ marginTop: 24, padding: 16 }}>
      
      {/* Zoom controls */}
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }}>
        <h2 style={{ fontSize: '1.2rem', margin: 0 }}>Timeline Setup</h2>
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          {(['2H', '6H', 'Arbeitszeit', 'Nachts', 'Tag', 'Woche', 'Monat'] as ViewMode[]).map(v => (
            <button 
              key={v}
              className={`btn ${view === v ? 'btn-primary' : 'btn-ghost'} btn-sm`}
              onClick={() => setView(v)}
            >
              {v}
            </button>
          ))}
        </div>
      </div>

      <div className="gantt-scroll-window">
        {/* Header markers */}
        <div className="gantt-header">
          {markers.map(m => (
            <div key={m.key} className="gantt-tick" style={{ left: `${m.pct}%` }}>
              <span className="gantt-tick-label">{m.label}</span>
            </div>
          ))}
        </div>

        {/* Chart body */}
        <div className="gantt-body">
          {isNowVisible && (
            <div style={{ position: 'absolute', top: 0, bottom: 0, left: 170, right: 0, pointerEvents: 'none', zIndex: 100 }}>
              <div className="gantt-now-line" style={{ left: `${nowPct}%` }} />
            </div>
          )}

          {Array.from(serverGroups.entries())
            .sort(([sNameA], [sNameB]) => {
              if (!servers) return 0
              const sA = servers.find(s => s.Name === sNameA)
              const sB = servers.find(s => s.Name === sNameB)
              return (sA?.Sortierung ?? 9999) - (sB?.Sortierung ?? 9999)
            })
            .map(([serverName, procs]) => {
            const sortedProcs = [...procs].sort((a, b) => (a.Sortierung ?? 9999) - (b.Sortierung ?? 9999))
            
            return (
            <div key={serverName} className="gantt-row">
              <div className="gantt-row-info" style={{ flexDirection: 'column', alignItems: 'flex-start', justifyContent: 'center', gap: 4 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontWeight: 'bold' }}>
                  {SERVER_ICON}
                  <span>{serverName}</span>
                </div>
                {sortedProcs.length > 0 && (
                  <div style={{ fontSize: '9px', color: 'var(--fg-muted)', display: 'flex', gap: 4, flexWrap: 'wrap' }}>
                    {sortedProcs.map(p => (
                      <span key={p.id} style={{ display: 'flex', alignItems: 'center', gap: 2 }}>
                        <span className="color-dot" style={{ backgroundColor: p.Farbe || '#3b82f6', width: 6, height: 6 }} />
                        {p.Name}
                      </span>
                    ))}
                  </div>
                )}
              </div>
              <div className="gantt-bars-area">
                {sortedProcs.map(p => (
                  <React.Fragment key={p.id}>
                    {renderBlocksForProcess(p)}
                  </React.Fragment>
                ))}
              </div>
            </div>
          )})}

          {serverGroups.size === 0 && (
            <div style={{ padding: 32, textAlign: 'center', color: 'var(--fg-muted)' }}>
              Keine aktiven Prozesse in dieser Ansicht sichtbar.
            </div>
          )}
        </div>
      </div>
    </div>
  )
}
