import { FormEvent, useEffect, useMemo, useState } from 'react';
import {
  createCategory,
  createTodo,
  deleteCategory,
  deleteTodo,
  getCategories,
  getTodos,
  updateCategory,
  updateTodo,
} from './api';
import type { Category, Todo } from './types';

const DEFAULT_COLOR = '#6366f1';
const COLORS = [DEFAULT_COLOR, '#0ea5e9', '#f97316', '#10b981', '#ec4899'];

export function App() {
  const [categories, setCategories] = useState<Category[]>([]);
  const [todos, setTodos] = useState<Todo[]>([]);
  const [selectedCategoryId, setSelectedCategoryId] = useState<number | null>(null);
  const [draft, setDraft] = useState('');
  const [draftCategoryId, setDraftCategoryId] = useState<number | null>(null);
  const [categoryName, setCategoryName] = useState('');
  const [categoryColor, setCategoryColor] = useState<string>(DEFAULT_COLOR);
  const [editingCategoryId, setEditingCategoryId] = useState<number | null>(null);
  const [error, setError] = useState<string | null>(null);

  const selectedCategory = useMemo(
    () => categories.find(category => category.id === selectedCategoryId) ?? null,
    [categories, selectedCategoryId],
  );

  const totalCount = useMemo(
    () => categories.reduce((total, category) => total + category.todo_count, 0),
    [categories],
  );

  async function load() {
    const [nextCategories, nextTodos] = await Promise.all([
      getCategories(),
      getTodos(selectedCategoryId),
    ]);
    setCategories(nextCategories);
    setTodos(nextTodos);
  }

  useEffect(() => {
    load().catch(handleError);
  }, [selectedCategoryId]);

  function handleError(err: unknown) {
    setError(err instanceof Error ? err.message : String(err));
  }

  async function onSubmit(event: FormEvent) {
    event.preventDefault();
    const title = draft.trim();
    if (!title) return;
    try {
      await createTodo(title, selectedCategoryId ?? draftCategoryId);
      setDraft('');
      await load();
    } catch (err) {
      handleError(err);
    }
  }

  async function onCategorySubmit(event: FormEvent) {
    event.preventDefault();
    const name = categoryName.trim();
    if (!name) return;
    try {
      if (editingCategoryId !== null) {
        await updateCategory(editingCategoryId, { name, color: categoryColor });
      } else {
        await createCategory(name, categoryColor);
      }
      setCategoryName('');
      setCategoryColor(DEFAULT_COLOR);
      setEditingCategoryId(null);
      await load();
    } catch (err) {
      handleError(err);
    }
  }

  function startEditCategory(category: Category) {
    setEditingCategoryId(category.id);
    setCategoryName(category.name);
    setCategoryColor(category.color);
  }

  async function removeCategory(categoryId: number) {
    try {
      await deleteCategory(categoryId);
      if (selectedCategoryId === categoryId) setSelectedCategoryId(null);
      await load();
    } catch (err) {
      handleError(err);
    }
  }

  return (
    <main className="grid min-h-screen grid-cols-[280px_1fr]">
      <aside className="flex flex-col gap-2 border-r border-border bg-surface p-8">
        <p className="muted text-sm">{{APP_NAME}}</p>
        <h1 className="mb-4">Categories</h1>

        <button
          className={`btn justify-start ${!selectedCategoryId ? 'btn-primary' : ''}`}
          onClick={() => setSelectedCategoryId(null)}
        >
          <span className="size-2.5 rounded-full bg-fg" />
          <span className="flex-1 text-left">All tasks</span>
          <small className="muted">{totalCount}</small>
        </button>

        {categories.map(category => (
          <div className="row gap-1.5" key={category.id}>
            <button
              className={`btn flex-1 justify-start ${selectedCategoryId === category.id ? 'btn-primary' : ''}`}
              onClick={() => setSelectedCategoryId(category.id)}
            >
              <span className="size-2.5 rounded-full" style={{ background: category.color }} />
              <span className="flex-1 text-left">{category.name}</span>
              <small className="muted">{category.todo_count}</small>
            </button>
            <button className="btn px-2.5" onClick={() => startEditCategory(category)}>Edit</button>
          </div>
        ))}

        <form className="stack mt-4 border-t border-border pt-4" onSubmit={onCategorySubmit}>
          <input
            className="input"
            value={categoryName}
            onChange={event => setCategoryName(event.target.value)}
            placeholder={editingCategoryId ? 'Rename category' : 'New category'}
          />
          <div className="row">
            {COLORS.map(color => (
              <button
                key={color}
                type="button"
                className={`size-6 rounded-full border-2 ${categoryColor === color ? 'border-fg' : 'border-transparent'}`}
                style={{ background: color }}
                onClick={() => setCategoryColor(color)}
              />
            ))}
          </div>
          <div className="row">
            <button type="submit" className="btn btn-primary">{editingCategoryId ? 'Save' : 'Add category'}</button>
            {editingCategoryId !== null ? (
              <button type="button" className="btn" onClick={() => removeCategory(editingCategoryId)}>
                Delete
              </button>
            ) : null}
          </div>
        </form>
      </aside>

      <section className="flex flex-col gap-6 p-12">
        <header className="stack gap-1">
          <p className="muted text-sm">{selectedCategory?.name ?? 'All categories'}</p>
          <h2>{selectedCategory ? `${selectedCategory.name} tasks` : 'Today\'s tasks'}</h2>
        </header>

        <form className="row" onSubmit={onSubmit}>
          <input
            className="input flex-1"
            value={draft}
            onChange={event => setDraft(event.target.value)}
            placeholder="Add a task..."
          />
          {!selectedCategoryId ? (
            <select
              className="input w-auto"
              value={draftCategoryId ?? ''}
              onChange={event => setDraftCategoryId(event.target.value ? Number(event.target.value) : null)}
            >
              <option value="">No category</option>
              {categories.map(category => (
                <option key={category.id} value={category.id}>{category.name}</option>
              ))}
            </select>
          ) : null}
          <button type="submit" className="btn btn-primary">Add</button>
        </form>

        {error ? <p className="text-danger">{error}</p> : null}

        <div className="stack">
          {todos.map(todo => (
            <article
              key={todo.id}
              className={`card row ${todo.completed ? 'opacity-70' : ''}`}
            >
              <label className="row flex-1 cursor-pointer">
                <input
                  type="checkbox"
                  checked={todo.completed}
                  onChange={() => updateTodo(todo.id, { completed: !todo.completed }).then(load).catch(handleError)}
                />
                <span className={todo.completed ? 'muted line-through' : ''}>{todo.title}</span>
              </label>
              {todo.category_name ? (
                <span
                  className="rounded-full px-2.5 py-1 text-xs text-accent-fg"
                  style={{ background: todo.category_color ?? '#6366f1' }}
                >
                  {todo.category_name}
                </span>
              ) : null}
              <button className="btn px-3 py-1.5" onClick={() => deleteTodo(todo.id).then(load).catch(handleError)}>Delete</button>
            </article>
          ))}
          {todos.length === 0 ? <p className="muted">No tasks yet. Add one above.</p> : null}
        </div>
      </section>
    </main>
  );
}
