<script lang="ts">
  import { appRpc } from './rpc.ts';
  import { appState, type Task } from './app-state.svelte.ts';

  interface Props {
    groupId: string;
    projectId: string;
    /** Agent ID perspective for creating tasks (defaults to 'admin'). */
    agentId?: string;
  }

  let { groupId, projectId, agentId = 'admin' }: Props = $props();

  // Read task state from project state tree
  function getTasks() { return appState.project.getState(projectId).tasks; }

  // ── Constants ──────────────────────────────────────────────────────

  const COLUMNS = ['todo', 'progress', 'review', 'done', 'blocked'] as const;
  const COL_LABELS: Record<string, string> = {
    todo: 'To Do', progress: 'In Progress', review: 'Review',
    done: 'Done', blocked: 'Blocked',
  };
  const PRIORITY_COLORS: Record<string, string> = {
    high: 'var(--ctp-red)', medium: 'var(--ctp-peach)',
    low: 'var(--ctp-teal)',
  };

  // ── Derived ──────────────────────────────────────────────────────────

  function getTasksByCol(): Record<string, Task[]> {
    return COLUMNS.reduce((acc, col) => {
      acc[col] = getTasks().tasks.filter(t => t.status === col);
      return acc;
    }, {} as Record<string, Task[]>);
  }

  // ── Data fetching ────────────────────────────────────────────────────

  async function loadTasks() {
    const tokenAtStart = appState.project.tasks.nextPollToken(projectId);
    try {
      const res = await appRpc.request['bttask.listTasks']({ groupId });
      if (tokenAtStart < appState.project.tasks.getPollToken(projectId)) return;
      appState.project.tasks.setState(projectId, 'tasks', res.tasks);
    } catch (err) {
      console.error('[TaskBoard] loadTasks:', err);
    }
  }

  async function createTask() {
    const t = getTasks();
    const title = t.newTitle.trim();
    if (!title) { appState.project.tasks.setState(projectId, 'error', 'Title required'); return; }
    appState.project.tasks.setState(projectId, 'error', '');

    try {
      const res = await appRpc.request['bttask.createTask']({
        title,
        description: t.newDesc.trim(),
        priority: t.newPriority,
        groupId,
        createdBy: agentId,
      });
      if (res.ok) {
        appState.project.tasks.setMulti(projectId, {
          newTitle: '', newDesc: '', newPriority: 'medium', showCreateForm: false,
        });
        await loadTasks();
      } else {
        appState.project.tasks.setState(projectId, 'error', res.error ?? 'Failed to create task');
      }
    } catch (err) {
      console.error('[TaskBoard] createTask:', err);
      appState.project.tasks.setState(projectId, 'error', 'Failed to create task');
    }
  }

  async function moveTask(taskId: string, newStatus: string) {
    const t = getTasks();
    const task = t.tasks.find(tk => tk.id === taskId);
    if (!task || task.status === newStatus) return;

    appState.project.tasks.nextPollToken(projectId); // Invalidate in-flight polls
    try {
      const res = await appRpc.request['bttask.updateTaskStatus']({
        taskId,
        status: newStatus,
        expectedVersion: task.version,
      });
      if (res.ok) {
        task.status = newStatus;
        task.version = res.newVersion ?? task.version + 1;
        appState.project.tasks.setState(projectId, 'tasks', [...t.tasks]);
      } else {
        appState.project.tasks.setState(projectId, 'error', res.error ?? 'Version conflict');
        await loadTasks();
      }
    } catch (err) {
      console.error('[TaskBoard] moveTask:', err);
      await loadTasks();
    }
  }

  async function deleteTask(taskId: string) {
    try {
      await appRpc.request['bttask.deleteTask']({ taskId });
      appState.project.tasks.setState(projectId, 'tasks',
        getTasks().tasks.filter(t => t.id !== taskId));
    } catch (err) {
      console.error('[TaskBoard] deleteTask:', err);
    }
  }

  // ── Drag handlers ────────────────────────────────────────────────────

  function onDragStart(e: DragEvent, taskId: string) {
    appState.project.tasks.setState(projectId, 'draggedTaskId', taskId);
    if (e.dataTransfer) {
      e.dataTransfer.effectAllowed = 'move';
      e.dataTransfer.setData('text/plain', taskId);
    }
  }

  function onDragOver(e: DragEvent, col: string) {
    e.preventDefault();
    appState.project.tasks.setState(projectId, 'dragOverCol', col);
  }

  function onDragLeave() {
    appState.project.tasks.setState(projectId, 'dragOverCol', null);
  }

  function onDrop(e: DragEvent, col: string) {
    e.preventDefault();
    appState.project.tasks.setState(projectId, 'dragOverCol', null);
    const t = getTasks();
    if (t.draggedTaskId) {
      moveTask(t.draggedTaskId, col);
      appState.project.tasks.setState(projectId, 'draggedTaskId', null);
    }
  }

  function onDragEnd() {
    appState.project.tasks.setMulti(projectId, { draggedTaskId: null, dragOverCol: null });
  }

  // ── Init + event-driven updates (Feature 4) ─────────────────────────

  let pollTimer: ReturnType<typeof setInterval> | null = null;

  function onTaskChanged(payload: { groupId: string }) {
    if (!payload.groupId || payload.groupId === groupId) loadTasks();
  }

  import { onMount } from 'svelte';
  onMount(() => {
    loadTasks();
    appRpc.addMessageListener('bttask.changed', onTaskChanged);
    pollTimer = setInterval(loadTasks, 30000);
    return () => {
      if (pollTimer) clearInterval(pollTimer);
      appRpc.removeMessageListener?.('bttask.changed', onTaskChanged);
    };
  });
</script>

<div class="task-board">
  <!-- Toolbar -->
  <div class="tb-toolbar">
    <span class="tb-title">Task Board</span>
    <span class="tb-count">{getTasks().tasks.length} tasks</span>
    <button class="tb-add-btn" onclick={() => { appState.project.tasks.setState(projectId, 'showCreateForm', !getTasks().showCreateForm); }}>
      {getTasks().showCreateForm ? 'Cancel' : '+ Task'}
    </button>
  </div>

  <!-- Create form -->
  {#if getTasks().showCreateForm}
    <div class="tb-create-form">
      <input
        class="tb-input"
        type="text"
        placeholder="Task title"
        value={getTasks().newTitle}
        oninput={(e) => appState.project.tasks.setState(projectId, 'newTitle', (e.target as HTMLInputElement).value)}
        onkeydown={(e) => { if (e.key === 'Enter') createTask(); }}
      />
      <input
        class="tb-input tb-desc"
        type="text"
        placeholder="Description (optional)"
        value={getTasks().newDesc}
        oninput={(e) => appState.project.tasks.setState(projectId, 'newDesc', (e.target as HTMLInputElement).value)}
      />
      <div class="tb-form-row">
        <select
          class="tb-select"
          value={getTasks().newPriority}
          onchange={(e) => appState.project.tasks.setState(projectId, 'newPriority', (e.target as HTMLSelectElement).value)}
        >
          <option value="low">Low</option>
          <option value="medium">Medium</option>
          <option value="high">High</option>
        </select>
        <button class="tb-submit" onclick={createTask}>Create</button>
      </div>
      {#if getTasks().error}
        <span class="tb-error">{getTasks().error}</span>
      {/if}
    </div>
  {/if}

  <!-- Kanban columns -->
  <div class="tb-columns">
    {#each COLUMNS as col}
      <div
        class="tb-column"
        class:drag-over={getTasks().dragOverCol === col}
        ondragover={(e) => onDragOver(e, col)}
        ondragleave={onDragLeave}
        ondrop={(e) => onDrop(e, col)}
        role="list"
        aria-label="{COL_LABELS[col]} column"
      >
        <div class="tb-col-header">
          <span class="tb-col-label">{COL_LABELS[col]}</span>
          <span class="tb-col-count">{getTasksByCol()[col]?.length ?? 0}</span>
        </div>

        <div class="tb-col-body">
          {#each getTasksByCol()[col] ?? [] as task (task.id)}
            <div
              class="tb-card"
              class:dragging={getTasks().draggedTaskId === task.id}
              draggable="true"
              ondragstart={(e) => onDragStart(e, task.id)}
              ondragend={onDragEnd}
              role="listitem"
            >
              <div class="card-header">
                <span
                  class="priority-dot"
                  style:background={PRIORITY_COLORS[task.priority] ?? 'var(--ctp-overlay0)'}
                  title="Priority: {task.priority}"
                ></span>
                <span class="card-title">{task.title}</span>
                <button
                  class="card-delete"
                  onclick={() => deleteTask(task.id)}
                  title="Delete task"
                  aria-label="Delete task"
                >&times;</button>
              </div>
              {#if task.description}
                <div class="card-desc">{task.description}</div>
              {/if}
              {#if task.assignedTo}
                <div class="card-assignee">
                  <span class="assignee-icon">@</span>
                  {task.assignedTo}
                </div>
              {/if}
            </div>
          {/each}
        </div>
      </div>
    {/each}
  </div>
</div>

<style>
  .task-board {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    overflow: hidden;
  }

  .tb-toolbar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 0.625rem;
    background: var(--ctp-mantle);
    border-bottom: 1px solid var(--ctp-surface0);
    flex-shrink: 0;
  }

  .tb-title {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--ctp-text);
  }

  .tb-count {
    font-size: 0.6875rem;
    color: var(--ctp-overlay0);
    margin-left: auto;
  }

  .tb-add-btn {
    padding: 0.125rem 0.5rem;
    background: color-mix(in srgb, var(--ctp-green) 15%, transparent);
    border: 1px solid var(--ctp-green);
    border-radius: 0.25rem;
    color: var(--ctp-green);
    font-family: var(--ui-font-family);
    font-size: 0.6875rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.1s;
  }

  .tb-add-btn:hover {
    background: color-mix(in srgb, var(--ctp-green) 30%, transparent);
  }

  .tb-create-form {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.5rem 0.625rem;
    background: var(--ctp-mantle);
    border-bottom: 1px solid var(--ctp-surface0);
    flex-shrink: 0;
  }

  .tb-input {
    height: 1.625rem;
    background: var(--ctp-surface0);
    border: 1px solid var(--ctp-surface1);
    border-radius: 0.25rem;
    color: var(--ctp-text);
    font-family: var(--ui-font-family);
    font-size: 0.75rem;
    padding: 0 0.375rem;
    outline: none;
  }

  .tb-input:focus { border-color: var(--ctp-mauve); }

  .tb-form-row {
    display: flex;
    gap: 0.25rem;
    align-items: center;
  }

  .tb-select {
    height: 1.625rem;
    background: var(--ctp-surface0);
    border: 1px solid var(--ctp-surface1);
    border-radius: 0.25rem;
    color: var(--ctp-text);
    font-family: var(--ui-font-family);
    font-size: 0.6875rem;
    padding: 0 0.25rem;
    outline: none;
  }

  .tb-submit {
    padding: 0.125rem 0.625rem;
    height: 1.625rem;
    background: color-mix(in srgb, var(--ctp-mauve) 20%, transparent);
    border: 1px solid var(--ctp-mauve);
    border-radius: 0.25rem;
    color: var(--ctp-mauve);
    font-family: var(--ui-font-family);
    font-size: 0.6875rem;
    font-weight: 600;
    cursor: pointer;
    margin-left: auto;
  }

  .tb-submit:hover {
    background: color-mix(in srgb, var(--ctp-mauve) 35%, transparent);
  }

  .tb-error {
    font-size: 0.6875rem;
    color: var(--ctp-red);
  }

  .tb-columns {
    display: flex;
    flex: 1;
    min-height: 0;
    overflow-x: auto;
    gap: 1px;
    background: var(--ctp-surface0);
  }

  .tb-column {
    flex: 1;
    min-width: 8rem;
    display: flex;
    flex-direction: column;
    background: var(--ctp-base);
    transition: background 0.15s;
  }

  .tb-column.drag-over {
    background: color-mix(in srgb, var(--ctp-blue) 8%, var(--ctp-base));
  }

  .tb-col-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.375rem 0.5rem;
    border-bottom: 1px solid var(--ctp-surface0);
    flex-shrink: 0;
  }

  .tb-col-label {
    font-size: 0.6875rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--ctp-subtext0);
  }

  .tb-col-count {
    font-size: 0.625rem;
    color: var(--ctp-overlay0);
    background: var(--ctp-surface0);
    padding: 0 0.25rem;
    border-radius: 0.25rem;
  }

  .tb-col-body {
    flex: 1;
    overflow-y: auto;
    padding: 0.375rem;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
  }

  .tb-card {
    background: var(--ctp-mantle);
    border: 1px solid var(--ctp-surface0);
    border-radius: 0.375rem;
    padding: 0.375rem 0.5rem;
    cursor: grab;
    transition: border-color 0.12s, opacity 0.12s;
  }

  .tb-card:hover { border-color: var(--ctp-surface1); }
  .tb-card.dragging { opacity: 0.4; }

  .card-header {
    display: flex;
    align-items: center;
    gap: 0.375rem;
  }

  .priority-dot {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    flex-shrink: 0;
  }

  .card-title {
    flex: 1;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--ctp-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .card-delete {
    background: transparent;
    border: none;
    color: var(--ctp-overlay0);
    font-size: 0.875rem;
    cursor: pointer;
    padding: 0;
    line-height: 1;
    flex-shrink: 0;
    opacity: 0;
    transition: opacity 0.1s, color 0.1s;
  }

  .tb-card:hover .card-delete { opacity: 1; }
  .card-delete:hover { color: var(--ctp-red); }

  .card-desc {
    font-size: 0.6875rem;
    color: var(--ctp-subtext0);
    margin-top: 0.125rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .card-assignee {
    font-size: 0.625rem;
    color: var(--ctp-overlay1);
    margin-top: 0.25rem;
    display: flex;
    align-items: center;
    gap: 0.125rem;
  }

  .assignee-icon {
    color: var(--ctp-blue);
    font-weight: 700;
  }
</style>
