'use client'

import { useEffect, useState, useTransition } from 'react'
import { useShallow } from 'zustand/react/shallow'
import {
  DndContext, DragEndEvent, DragOverlay, DragStartEvent,
  PointerSensor, useSensor, useSensors, closestCorners,
} from '@dnd-kit/core'
import { toast } from 'sonner'
import { useSoloStore } from '@/stores/solo-store'
import {
  selectSoloTaskById,
  selectSoloTasksForColumn,
  selectSoloUnassignedStories,
} from '@/stores/solo-workspace/selectors'
import type { SoloTask, SoloUnassignedStory, SoloWorkspaceSnapshot } from '@/stores/solo-workspace/types'
import { taskStatusToApi } from '@/lib/task-status'
import { previewEnqueueAllAction, enqueueClaudeJobsBatchAction } from '@/actions/claude-jobs'
import { BatchEnqueueBlockerDialog } from './batch-enqueue-blocker-dialog'
import { debugProps } from '@/lib/debug'
import { Button } from '@/components/ui/button'
import { DemoTooltip } from '@/components/shared/demo-tooltip'
import { SplitPane } from '@/components/split-pane/split-pane'
import { SoloColumn, type ColumnStatus } from './solo-column'
import { SoloTaskCardOverlay } from './solo-task-card'
import { TaskDetailDialog } from './task-detail-dialog'
import { UnassignedStoriesSheet } from './unassigned-stories-sheet'

export type { SoloTask } from '@/stores/solo-workspace/types'

export interface SoloBoardProps {
  productId: string
  sprintGoal: string
  tasks?: SoloTask[]
  unassignedStories?: SoloUnassignedStory[]
  isDemo: boolean
  currentUserId?: string
  repoUrl?: string | null
}

const COLUMN_STATUSES: ColumnStatus[] = ['TO_DO', 'IN_PROGRESS', 'DONE']

function getColumnStatus(status: SoloTask['status']): ColumnStatus {
  if (status === 'REVIEW') return 'IN_PROGRESS'
  return status
}

export function SoloBoard({
  productId, sprintGoal, tasks: initialTasks, unassignedStories: initialUnassigned, isDemo, repoUrl, currentUserId,
}: SoloBoardProps) {
  const {
    tasks,
    hydrateSnapshot,
    optimisticMove,
    rollback,
    markPending,
    clearPending,
    removeUnassignedStory,
  } = useSoloStore()
  const claudeJobsByTaskId = useSoloStore((s) => s.claudeJobsByTaskId)
  const [activeDragId, setActiveDragId] = useState<string | null>(null)
  const [selectedTaskId, setSelectedTaskId] = useState<string | null>(null)
  const selectedTask = useSoloStore(selectSoloTaskById(selectedTaskId))
  const [sheetOpen, setSheetOpen] = useState(false)
  const [, startTransition] = useTransition()
  const [batchPending, startBatchTransition] = useTransition()
  const [confirmPending, startConfirmTransition] = useTransition()

  type BlockerDialogState = {
    prefixCount: number
    blockerReason: 'task-review' | 'pbi-blocked'
    blockerLabel: string
    prefixIds: string[]
  }
  const [blockerDialog, setBlockerDialog] = useState<BlockerDialogState | null>(null)

  useEffect(() => {
    if (!initialTasks || !initialUnassigned || !currentUserId) return
    const snapshot: SoloWorkspaceSnapshot = {
      product: { id: productId, name: '' },
      sprint: { id: `compat:${productId}`, sprint_goal: sprintGoal },
      activeUserId: currentUserId,
      tasks: initialTasks,
      unassignedStories: initialUnassigned,
    }
    hydrateSnapshot(snapshot)
  }, [currentUserId, hydrateSnapshot, initialTasks, initialUnassigned, productId, sprintGoal])

  const pointerSensor = useSensor(PointerSensor, { activationConstraint: { distance: 5 } })
  const sensors = useSensors(...(isDemo ? [] : [pointerSensor]))

  const columnTasks: Record<ColumnStatus, SoloTask[]> = {
    TO_DO: useSoloStore(useShallow(selectSoloTasksForColumn('TO_DO'))),
    IN_PROGRESS: useSoloStore(useShallow(selectSoloTasksForColumn('IN_PROGRESS'))),
    DONE: useSoloStore(useShallow(selectSoloTasksForColumn('DONE'))),
  }
  const unassignedStories = useSoloStore(useShallow(selectSoloUnassignedStories))

  function handleDragStart(event: DragStartEvent) {
    setActiveDragId(event.active.id as string)
  }

  function handleDragEnd(event: DragEndEvent) {
    setActiveDragId(null)
    const { active, over } = event
    if (!over) return

    const toStatus = over.id as ColumnStatus
    if (!COLUMN_STATUSES.includes(toStatus)) return

    const taskId = active.id as string
    const task = tasks[taskId]
    if (!task) return
    if (getColumnStatus(task.status) === toStatus) return

    const prevStatus = optimisticMove(taskId, toStatus)
    if (!prevStatus) return

    // Onderdruk realtime-echo van onze eigen write — de Postgres-trigger
    // vuurt en die NOTIFY komt zo terug via SSE; zonder pending-marker
    // zou de store nogmaals een set() doen of de optimistic state
    // overschrijven. clearPending na de fetch (succes of fail).
    //
    // We gebruiken bewust een fetch-based Route Handler in plaats van
    // de updateTaskStatusAction Server Action — Server Actions
    // triggeren een full route-tree refresh die de open SSE-stream van
    // /api/realtime/solo zou afkappen, waardoor we elke 5s reconnecten
    // en realtime-events missen.
    markPending(taskId)
    startTransition(async () => {
      try {
        const res = await fetch(`/api/tasks/${taskId}`, {
          method: 'PATCH',
          headers: { 'Content-Type': 'application/json' },
          credentials: 'include',
          body: JSON.stringify({ status: taskStatusToApi(toStatus) }),
        })
        if (!res.ok) {
          rollback(taskId, prevStatus)
          toast.error('Status bijwerken mislukt — taak teruggeplaatst')
        }
      } catch {
        rollback(taskId, prevStatus)
        toast.error('Status bijwerken mislukt — taak teruggeplaatst')
      } finally {
        clearPending(taskId)
      }
    })
  }

  const activeTask = activeDragId ? tasks[activeDragId] : null

  const queueableCount = columnTasks.TO_DO.filter(t => {
    const job = claudeJobsByTaskId[t.id]
    return !job || (job.status !== 'queued' && job.status !== 'claimed' && job.status !== 'running')
  }).length

  function handleStartAll() {
    if (queueableCount === 0) return
    startBatchTransition(async () => {
      const preview = await previewEnqueueAllAction(productId)
      if ('error' in preview) {
        toast.error(preview.error)
        return
      }
      if (preview.blockerIndex === null) {
        const todoIds = preview.tasks.filter(t => t.status === 'TO_DO').map(t => t.id)
        const result = await enqueueClaudeJobsBatchAction(productId, todoIds)
        if ('error' in result) {
          toast.error(result.error)
        } else if (result.count === 0) {
          toast.info('Geen taken om te starten')
        } else {
          toast.success(`${result.count} ${result.count === 1 ? 'agent' : 'agents'} ingeschakeld`)
        }
      } else {
        const blockerTask = preview.tasks[preview.blockerIndex]
        const blockerLabel = preview.blockerReason === 'task-review'
          ? `${blockerTask.story_title} — ${blockerTask.title}`
          : blockerTask.story_title
        setBlockerDialog({
          prefixCount: preview.blockerIndex,
          blockerReason: preview.blockerReason!,
          blockerLabel,
          prefixIds: preview.tasks.slice(0, preview.blockerIndex).map(t => t.id),
        })
      }
    })
  }

  function handleBlockerConfirm() {
    if (!blockerDialog) return
    const { prefixIds } = blockerDialog
    setBlockerDialog(null)
    startConfirmTransition(async () => {
      const result = await enqueueClaudeJobsBatchAction(productId, prefixIds)
      if ('error' in result) {
        toast.error(result.error)
      } else if (result.count === 0) {
        toast.info('Geen taken om te starten')
      } else {
        toast.success(`${result.count} ${result.count === 1 ? 'agent' : 'agents'} ingeschakeld`)
      }
    })
  }

  return (
    <div className="flex flex-col h-full p-4 gap-4 min-h-0" {...debugProps('solo-board', 'SoloBoard', 'components/solo/solo-board.tsx')}>
      <div className="flex items-start justify-between gap-4 shrink-0" data-debug-id="solo-board__header">
        <div className="min-w-0 flex items-center gap-3">
          <DemoTooltip show={isDemo}>
            <Button
              size="sm"
              onClick={handleStartAll}
              disabled={isDemo || batchPending || confirmPending || queueableCount === 0}
            >
              {batchPending || confirmPending ? 'Starten…' : `Start agents (${queueableCount})`}
            </Button>
          </DemoTooltip>
          {sprintGoal && (
            <p className="text-sm text-muted-foreground line-clamp-2 min-w-0">{sprintGoal}</p>
          )}
        </div>
        <button
          className="text-sm text-primary hover:underline whitespace-nowrap shrink-0 disabled:text-muted-foreground disabled:cursor-default"
          disabled={unassignedStories.length === 0}
          onClick={() => setSheetOpen(true)}
        >
          Toon openstaande stories ({unassignedStories.length})
        </button>
      </div>

      <DndContext
        sensors={sensors}
        collisionDetection={closestCorners}
        onDragStart={handleDragStart}
        onDragEnd={handleDragEnd}
      >
        <div className="flex-1 min-h-0" data-debug-id="solo-board__columns">
          <SplitPane
            cookieKey={`solo-${productId}`}
            defaultSplit={[33, 33, 34]}
            tabLabels={['Te doen', 'Bezig', 'Klaar']}
            panes={[
              <SoloColumn
                key="TO_DO"
                status="TO_DO"
                tasks={columnTasks.TO_DO}
                isDemo={isDemo}
                onTaskClick={(t) => setSelectedTaskId(t.id)}
              />,
              <SoloColumn
                key="IN_PROGRESS"
                status="IN_PROGRESS"
                tasks={columnTasks.IN_PROGRESS}
                isDemo={isDemo}
                onTaskClick={(t) => setSelectedTaskId(t.id)}
              />,
              <SoloColumn
                key="DONE"
                status="DONE"
                tasks={columnTasks.DONE}
                isDemo={isDemo}
                onTaskClick={(t) => setSelectedTaskId(t.id)}
              />,
            ]}
          />
        </div>
        <DragOverlay>
          {activeTask && <SoloTaskCardOverlay task={activeTask} />}
        </DragOverlay>
      </DndContext>

      <TaskDetailDialog
        task={selectedTask}
        productId={productId}
        isDemo={isDemo}
        repoUrl={repoUrl}
        onClose={() => setSelectedTaskId(null)}
      />

      <UnassignedStoriesSheet
        stories={unassignedStories}
        productId={productId}
        isDemo={isDemo}
        open={sheetOpen}
        onOpenChange={setSheetOpen}
        onClaim={removeUnassignedStory}
      />

      {blockerDialog && (
        <BatchEnqueueBlockerDialog
          open
          onOpenChange={(v) => { if (!v) setBlockerDialog(null) }}
          prefixCount={blockerDialog.prefixCount}
          blockerReason={blockerDialog.blockerReason}
          blockerLabel={blockerDialog.blockerLabel}
          onConfirm={handleBlockerConfirm}
          onCancel={() => setBlockerDialog(null)}
        />
      )}
    </div>
  )
}
