<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import draggable from 'vuedraggable'
import CardRow from '../components/CardRow.vue'
import KanbanCard from '../components/KanbanCard.vue'
import ProjectManageMenu from '../components/ProjectManageMenu.vue'
import { ApiError, apiRequest } from '../lib/api'
import { reorderColumn, type ColumnChange } from '../lib/cardOrder'
import { useCardsStore } from '../stores/cards'
import { useInboxStore } from '../stores/inbox'
import type { Card, CardStatus, Project } from '../types'

const route = useRoute()
const cards = useCardsStore()
const inbox = useInboxStore()

const projectId = Number(route.params.id)
const project = ref<Project | null>(null)
const statuses = ref<CardStatus[]>([])
const loadError = ref<string | null>(null)
const actionError = ref<string | null>(null)

// Tab order is unchanged (All tasks first); Kanban is just the default view.
const tabs = [
  { key: 'all', label: 'All tasks' },
  { key: 'kanban', label: 'Kanban' },
] as const
const activeTab = ref<(typeof tabs)[number]['key']>('kanban')

const newText = ref('')
const submitting = ref(false)

const summary = computed(() => {
  const total = cards.cards.length
  if (total === 0) return 'No cards yet.'
  return `${total} card${total === 1 ? '' : 's'}.`
})

// The "all tasks" list has no manual order — sort by name, case-insensitively.
const sortedCards = computed(() =>
  [...cards.cards].sort((a, b) => a.text.localeCompare(b.text, undefined, { sensitivity: 'base' })),
)

// --- Kanban board --------------------------------------------------------
// One column per project status -- the inbox lives in the sidebar now, not
// here, though it's still a valid drag source/target (shared "kanban" group).
interface Column {
  key: string
  title: string
  statusId: number
  cards: Card[]
}

const board = ref<Column[]>([])

function buildColumns(): Column[] {
  return statuses.value.map((s) => ({
    key: `status-${s.id}`,
    title: s.name,
    statusId: s.id,
    cards: cards.cards.filter((card) => card.status_id === s.id),
  }))
}

function rebuildBoard() {
  board.value = buildColumns()
}

// Re-derive the columns whenever the underlying cards or the status set change
// (e.g. after a move is persisted, or a failed move is rolled back).
watch([() => cards.cards, statuses], rebuildBoard, { deep: true })

async function onColumnChange(change: ColumnChange, column: Column) {
  // `added` (card dragged in, from another column here or from the sidebar's
  // inbox) or `moved` (reordered within this one): persist this column's new
  // id order. The column it left -- another status, or the inbox -- is
  // re-packed server-side. `removed` needs no action here.
  if (!change.added && !change.moved) return

  actionError.value = null
  try {
    await reorderColumn(projectId, column.statusId, column.cards.map((c) => c.id))
  } catch (e) {
    actionError.value = e instanceof ApiError ? e.message : 'Something went wrong.'
  } finally {
    // Either side of the drag could have been the inbox, so refresh both.
    await Promise.all([inbox.load(), cards.load(projectId)])
  }
}

onMounted(() => void load())

async function load() {
  loadError.value = null
  try {
    const [{ project: fetched }, { statuses: fetchedStatuses }] = await Promise.all([
      apiRequest<{ project: Project }>(`/projects/${projectId}`, { auth: true }),
      apiRequest<{ statuses: CardStatus[] }>(`/projects/${projectId}/statuses`, { auth: true }),
      cards.load(projectId),
    ])
    project.value = fetched
    statuses.value = fetchedStatuses
    rebuildBoard()
  } catch (e) {
    if (e instanceof ApiError && e.status === 404) {
      loadError.value = 'That project does not exist.'
    } else {
      loadError.value = e instanceof ApiError ? e.message : 'Could not load the project.'
    }
  }
}

/** Run a store mutation, surfacing failures and resyncing from the server. */
async function run(op: Promise<unknown>) {
  actionError.value = null
  try {
    await op
  } catch (e) {
    actionError.value = e instanceof ApiError ? e.message : 'Something went wrong.'
    await cards.load(projectId)
  }
}

async function onCreate() {
  submitting.value = true
  actionError.value = null
  try {
    await cards.add(newText.value)
    newText.value = ''
  } catch (e) {
    actionError.value = e instanceof ApiError ? e.message : 'Could not add the card.'
  } finally {
    submitting.value = false
  }
}

// --- add a card directly into one kanban column -- one draft per status, so
// typing in one column's form doesn't touch another's.
const newColumnCardText = ref<Record<number, string>>({})
const addingToStatusId = ref<number | null>(null)

async function onCreateInColumn(column: Column) {
  addingToStatusId.value = column.statusId
  actionError.value = null
  try {
    await cards.add(newColumnCardText.value[column.statusId] ?? '', column.statusId)
    newColumnCardText.value[column.statusId] = ''
  } catch (e) {
    actionError.value = e instanceof ApiError ? e.message : 'Could not add the card.'
  } finally {
    addingToStatusId.value = null
  }
}
</script>

<template>
  <section class="card project">
    <p v-if="loadError" class="form-error">{{ loadError }}</p>

    <template v-else-if="project">
      <div class="project-head">
        <h1 class="project-head__title">
          <RouterLink :to="{ name: 'dashboard' }" class="title-back" aria-label="Back to dashboard">&larr;</RouterLink>
          {{ project.title }}
        </h1>

        <div class="project-head__actions">
          <ProjectManageMenu
            :project-id="projectId"
            :project-title="project.title"
            :card-count="cards.cards.length"
          />
        </div>
      </div>

      <p v-if="actionError" class="form-error">{{ actionError }}</p>

      <div class="tabs" role="tablist">
        <button
          v-for="tab in tabs"
          :key="tab.key"
          type="button"
          role="tab"
          :aria-selected="activeTab === tab.key"
          class="tabs__tab"
          :class="{ 'tabs__tab--active': activeTab === tab.key }"
          @click="activeTab = tab.key"
        >
          {{ tab.label }}
        </button>
      </div>

      <!-- Tab: All tasks -->
      <div v-show="activeTab === 'all'" class="tabs__panel" role="tabpanel">
        <p class="muted">{{ summary }}</p>

        <p v-if="cards.loading && !cards.loaded" class="muted">Loading…</p>

        <ul v-else-if="sortedCards.length" class="cards">
          <CardRow
            v-for="card in sortedCards"
            :key="card.id"
            :card="card"
            @delete="run(cards.remove(card))"
          />
        </ul>

        <form class="kanban__new form--new-card" @submit.prevent="onCreate">
          <input
            v-model="newText"
            class="field field--compact"
            type="text"
            maxlength="1000"
            required
            placeholder="New card"
            aria-label="New card"
          />
          <button
            type="submit"
            class="btn-icon"
            :disabled="submitting"
            :title="submitting ? 'Adding…' : 'Add card'"
            :aria-label="submitting ? 'Adding…' : 'Add card'"
          >+</button>
        </form>
      </div>

      <!-- Tab: Kanban -->
      <div v-show="activeTab === 'kanban'" class="tabs__panel" role="tabpanel">
        <p v-if="cards.loading && !cards.loaded" class="muted">Loading…</p>

        <div v-else class="kanban">
          <section v-for="column in board" :key="column.key" class="kanban__col">
            <header class="kanban__head">
              <span class="kanban__title">{{ column.title }}</span>
              <span class="kanban__count">{{ column.cards.length }}</span>
            </header>

            <draggable
              :list="column.cards"
              :group="{ name: 'kanban' }"
              item-key="id"
              class="kanban__cards"
              ghost-class="kanban-card--ghost"
              :animation="150"
              @change="(e: ColumnChange) => onColumnChange(e, column)"
            >
              <template #item="{ element }: { element: Card }">
                <KanbanCard :card="element" />
              </template>
            </draggable>

            <form class="kanban__new" @submit.prevent="onCreateInColumn(column)">
              <input
                v-model="newColumnCardText[column.statusId]"
                class="field field--compact"
                type="text"
                maxlength="1000"
                required
                placeholder="New card"
                :aria-label="`New card in ${column.title}`"
              />
              <button
                type="submit"
                class="btn-icon"
                :disabled="addingToStatusId === column.statusId"
                :title="addingToStatusId === column.statusId ? 'Adding…' : `Add card to ${column.title}`"
                :aria-label="addingToStatusId === column.statusId ? 'Adding…' : `Add card to ${column.title}`"
              >+</button>
            </form>
          </section>
        </div>
      </div>
    </template>
  </section>
</template>
