import { FormEvent, MouseEvent, useEffect, useRef, useState } from 'react'

type TaskStatus = 'WAITING' | 'IN_PROGRESS' | 'COMPLETED'

type UserSummary = {
  id: string
  name: string
}

type Assignee = UserSummary

type Task = {
  id: string
  title: string
  description: string | null
  status: TaskStatus
  points: number
  assignee: Assignee | null
  createdAt: string
}

type ApiError = {
  message?: string
}

type TaskBoardProps = {
  activeUserName: string
  users: UserSummary[]
  onLogOut: () => void
}

const columns: { status: TaskStatus; title: string }[] = [
  { status: 'WAITING', title: 'Väntande' },
  { status: 'IN_PROGRESS', title: 'Pågående' },
  { status: 'COMPLETED', title: 'Klart' },
]

function TaskBoard({ activeUserName, users, onLogOut }: TaskBoardProps) {
  const [tasks, setTasks] = useState<Task[]>([])
  const [loadState, setLoadState] = useState<'loading' | 'ready' | 'error'>('loading')
  const [showCreateTask, setShowCreateTask] = useState(false)
  const [editingAssigneeTaskId, setEditingAssigneeTaskId] = useState<string | null>(null)
  const [savingAssigneeTaskIds, setSavingAssigneeTaskIds] = useState<Set<string>>(new Set())
  const [assignmentErrors, setAssignmentErrors] = useState<Record<string, string>>({})

  const loadTasks = async () => {
    setLoadState('loading')

    try {
      const response = await fetch('/api/tasks')

      if (!response.ok) {
        throw new Error('Kunde inte hämta uppgifter')
      }

      setTasks((await response.json()) as Task[])
      setLoadState('ready')
    } catch {
      setLoadState('error')
    }
  }

  useEffect(() => {
    void loadTasks()
  }, [])

  const updateAssignee = async (task: Task, assigneeId: string) => {
    if (savingAssigneeTaskIds.has(task.id)) {
      return
    }

    setSavingAssigneeTaskIds((current) => new Set(current).add(task.id))
    setAssignmentErrors((current) => ({ ...current, [task.id]: '' }))

    try {
      const response = await fetch(`/api/tasks/${task.id}/assignee`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ assigneeId: assigneeId || null }),
      })

      if (!response.ok) {
        const apiError = (await response.json().catch(() => ({}))) as ApiError
        setAssignmentErrors((current) => ({
          ...current,
          [task.id]: apiError.message ?? 'Det gick inte att ändra ansvarig. Försök igen.',
        }))
        return
      }

      const updatedTask = (await response.json()) as Task
      setTasks((current) =>
        current.map((currentTask) => (currentTask.id === updatedTask.id ? updatedTask : currentTask)),
      )
      setEditingAssigneeTaskId(null)
    } catch {
      setAssignmentErrors((current) => ({
        ...current,
        [task.id]: 'Det gick inte att ändra ansvarig. Försök igen.',
      }))
    } finally {
      setSavingAssigneeTaskIds((current) => {
        const next = new Set(current)
        next.delete(task.id)
        return next
      })
    }
  }

  return (
    <main className="task-app">
      <header className="app-header">
        <div>
          <p className="eyebrow">HemHub</p>
          <h1>Uppgifter</h1>
        </div>
        <div className="user-controls">
          <span>{activeUserName}</span>
          <button type="button" className="secondary compact" onClick={onLogOut}>
            Logga ut
          </button>
        </div>
      </header>

      <div className="board-toolbar">
        <div aria-live="polite">
          {loadState === 'loading' && 'Laddar uppgifter…'}
          {loadState === 'error' && (
            <>
              Kunde inte hämta uppgifter.
              <button type="button" className="link-button" onClick={() => void loadTasks()}>
                Försök igen
              </button>
            </>
          )}
        </div>
        <button type="button" onClick={() => setShowCreateTask(true)}>
          Ny uppgift
        </button>
      </div>

      <section className="board" aria-label="Uppgiftsbräda">
        {columns.map((column) => (
          <section className="board-column" key={column.status} aria-labelledby={column.status}>
            <h2 id={column.status}>{column.title}</h2>
            <div className="task-list">
              {tasks
                .filter((task) => task.status === column.status)
                .map((task) => (
                  <article className="task-card" key={task.id}>
                    <div className="task-card-header">
                      <h3>{task.title}</h3>
                      <span className="points-badge">{task.points} p</span>
                    </div>
                    {task.description && <p>{task.description}</p>}
                    <AssigneeControl
                      task={task}
                      users={users}
                      editing={editingAssigneeTaskId === task.id}
                      saving={savingAssigneeTaskIds.has(task.id)}
                      error={assignmentErrors[task.id]}
                      onEdit={() => setEditingAssigneeTaskId(task.id)}
                      onChange={(assigneeId) => void updateAssignee(task, assigneeId)}
                    />
                  </article>
                ))}
            </div>
          </section>
        ))}
      </section>

      {showCreateTask && (
        <CreateTaskModal
          users={users}
          onClose={() => setShowCreateTask(false)}
          onCreated={(task) => {
            setTasks((currentTasks) => [...currentTasks, task])
            setShowCreateTask(false)
          }}
        />
      )}
    </main>
  )
}

type AssigneeControlProps = {
  task: Task
  users: UserSummary[]
  editing: boolean
  saving: boolean
  error?: string
  onEdit: () => void
  onChange: (assigneeId: string) => void
}

function UserIcon() {
  return (
    <svg
      className="user-icon"
      viewBox="0 0 24 24"
      width="18"
      height="18"
      aria-hidden="true"
    >
      <circle cx="12" cy="8" r="3.5" fill="none" stroke="currentColor" strokeWidth="1.8" />
      <path
        d="M5 20c.5-4 3-6 7-6s6.5 2 7 6"
        fill="none"
        stroke="currentColor"
        strokeWidth="1.8"
        strokeLinecap="round"
      />
    </svg>
  )
}

function AssigneeControl({
  task,
  users,
  editing,
  saving,
  error,
  onEdit,
  onChange,
}: AssigneeControlProps) {
  const displayName = task.assignee?.name ?? (task.status === 'WAITING' ? 'Ta uppgift' : 'Otilldelad')

  if (task.status !== 'WAITING') {
    return (
      <div className="task-assignee task-assignee-static">
        <UserIcon />
        <span>{displayName}</span>
      </div>
    )
  }

  return (
    <div className="task-assignment">
      {editing ? (
        <label className="assignee-select-label">
          <span className="visually-hidden">Ansvarig för {task.title}</span>
          <UserIcon />
          <select
            aria-label={`Ansvarig för ${task.title}`}
            value={task.assignee?.id ?? ''}
            disabled={saving}
            autoFocus
            onChange={(event) => onChange(event.target.value)}
          >
            <option value="">Ingen</option>
            {users.map((user) => (
              <option key={user.id} value={user.id}>
                {user.name}
              </option>
            ))}
          </select>
        </label>
      ) : (
        <button
          type="button"
          className="task-assignee task-assignee-button"
          disabled={saving}
          onClick={onEdit}
          aria-label={`Ändra ansvarig för ${task.title}`}
        >
          <UserIcon />
          <span>{displayName}</span>
        </button>
      )}
      {error && (
        <p className="assignment-error error" role="alert">
          {error}
        </p>
      )}
    </div>
  )
}

type CreateTaskModalProps = {
  users: UserSummary[]
  onClose: () => void
  onCreated: (task: Task) => void
}

function CreateTaskModal({ users, onClose, onCreated }: CreateTaskModalProps) {
  const [title, setTitle] = useState('')
  const [description, setDescription] = useState('')
  const [points, setPoints] = useState('1')
  const [assigneeId, setAssigneeId] = useState('')
  const [error, setError] = useState('')
  const [isSubmitting, setIsSubmitting] = useState(false)
  const isSubmittingRef = useRef(false)

  useEffect(() => {
    const closeOnEscape = (event: KeyboardEvent) => {
      if (event.key === 'Escape' && !isSubmittingRef.current) {
        onClose()
      }
    }

    window.addEventListener('keydown', closeOnEscape)
    return () => window.removeEventListener('keydown', closeOnEscape)
  }, [onClose])

  const closeFromBackdrop = (event: MouseEvent<HTMLDivElement>) => {
    if (event.target === event.currentTarget && !isSubmittingRef.current) {
      onClose()
    }
  }

  const submit = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault()

    if (isSubmittingRef.current) {
      return
    }

    const trimmedTitle = title.trim()
    const trimmedDescription = description.trim()
    const numericPoints = Number(points)

    if (!trimmedTitle || [...trimmedTitle].length > 100) {
      setError('Titeln måste innehålla mellan 1 och 100 tecken.')
      return
    }

    if ([...trimmedDescription].length > 500) {
      setError('Beskrivningen får innehålla högst 500 tecken.')
      return
    }

    if (
      !points.trim() ||
      !Number.isInteger(numericPoints) ||
      numericPoints < 1 ||
      numericPoints > 99
    ) {
      setError('Poäng måste vara ett heltal mellan 1 och 99.')
      return
    }

    setError('')
    isSubmittingRef.current = true
    setIsSubmitting(true)

    try {
      const response = await fetch('/api/tasks', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          title: trimmedTitle,
          description: trimmedDescription || null,
          points: numericPoints,
          assigneeId: assigneeId || null,
        }),
      })

      if (!response.ok) {
        const apiError = (await response.json().catch(() => ({}))) as ApiError
        setError(apiError.message ?? 'Det gick inte att skapa uppgiften. Försök igen.')
        return
      }

      onCreated((await response.json()) as Task)
    } catch {
      setError('Det gick inte att skapa uppgiften. Försök igen.')
    } finally {
      isSubmittingRef.current = false
      setIsSubmitting(false)
    }
  }

  return (
    <div className="modal-backdrop" onMouseDown={closeFromBackdrop}>
      <section
        className="modal"
        role="dialog"
        aria-modal="true"
        aria-labelledby="create-task-title"
      >
        <div className="modal-header">
          <h2 id="create-task-title">Skapa ny uppgift</h2>
          <button
            type="button"
            className="close-button"
            aria-label="Stäng"
            disabled={isSubmitting}
            onClick={onClose}
          >
            ×
          </button>
        </div>
        <form noValidate onSubmit={(event) => void submit(event)}>
          <label htmlFor="task-title">Titel</label>
          <input
            id="task-title"
            autoFocus
            value={title}
            disabled={isSubmitting}
            onChange={(event) => setTitle(event.target.value)}
          />

          <label htmlFor="task-description">Beskrivning (valfri)</label>
          <textarea
            id="task-description"
            rows={5}
            value={description}
            disabled={isSubmitting}
            onChange={(event) => setDescription(event.target.value)}
          />

          <label htmlFor="task-points">Poäng</label>
          <input
            id="task-points"
            type="number"
            min="1"
            max="99"
            step="1"
            value={points}
            disabled={isSubmitting}
            aria-describedby="task-points-help"
            onChange={(event) => setPoints(event.target.value)}
          />
          <p id="task-points-help" className="field-help">
            1–99 poäng beroende på hur tidskrävande, besvärlig eller viktig uppgiften är.
          </p>

          <label className="field-label-uppercase" htmlFor="task-assignee">
            Tilldela
          </label>
          <select
            id="task-assignee"
            value={assigneeId}
            disabled={isSubmitting}
            onChange={(event) => setAssigneeId(event.target.value)}
          >
            <option value="">Ingen</option>
            {users.map((user) => (
              <option key={user.id} value={user.id}>
                {user.name}
              </option>
            ))}
          </select>

          {error && (
            <p className="error" role="alert">
              {error}
            </p>
          )}

          <button type="submit" disabled={isSubmitting}>
            {isSubmitting ? 'Skapar…' : 'Skapa uppgift'}
          </button>
        </form>
      </section>
    </div>
  )
}

export default TaskBoard
