<script setup lang="ts">
import { onMounted, computed, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useTasksStore } from "@/stores/tasks";
import { useNotesStore } from "@/stores/notes";
import { renderMarkdown } from "@/utils/markdown";
import { relativeTime } from "@/composables/useRelativeTime";
import { apiPost } from "@/api/client";
import type { Note } from "@/types/note";
import type { TaskStatus } from "@/types/task";
import StatusBadge from "@/components/StatusBadge.vue";
import PriorityBadge from "@/components/PriorityBadge.vue";
import TagPill from "@/components/TagPill.vue";

const route = useRoute();
const router = useRouter();
const store = useTasksStore();
const notesStore = useNotesStore();
const backlinks = ref<{ type: string; id: number; title: string }[]>([]);
const converting = ref(false);

const taskId = computed(() => Number(route.params.id));

async function loadTask(id: number) {
  backlinks.value = [];
  await store.fetchTask(id);
  try {
    backlinks.value = await notesStore.fetchBacklinks(id);
  } catch {
    backlinks.value = [];
  }
}

onMounted(() => loadTask(taskId.value));

watch(() => route.params.id, (newId) => {
  if (newId) loadTask(Number(newId));
});

const renderedBody = computed(() => {
  if (!store.currentTask) return "";
  return renderMarkdown(store.currentTask.body);
});

const statusCycle: Record<TaskStatus, TaskStatus> = {
  todo: "in_progress",
  in_progress: "done",
  done: "todo",
};

function cycleStatus() {
  if (!store.currentTask) return;
  store.patchStatus(
    store.currentTask.id,
    statusCycle[store.currentTask.status as TaskStatus]
  );
}

function isOverdue(): boolean {
  if (!store.currentTask?.due_date || store.currentTask.status === "done")
    return false;
  return (
    new Date(store.currentTask.due_date) < new Date(new Date().toDateString())
  );
}

async function convertToNote() {
  if (converting.value) return;
  converting.value = true;
  try {
    await notesStore.convertToNote(taskId.value);
    const { useToastStore } = await import("@/stores/toast");
    useToastStore().show("Converted to note");
    router.push(`/notes/${taskId.value}`);
  } catch {
    const { useToastStore } = await import("@/stores/toast");
    useToastStore().show("Failed to convert task", "error");
  } finally {
    converting.value = false;
  }
}

async function onBodyClick(e: MouseEvent) {
  const target = e.target as HTMLElement;

  const tagLink = target.closest(".inline-tag") as HTMLAnchorElement | null;
  if (tagLink) {
    e.preventDefault();
    const tag = tagLink.dataset.tag;
    if (tag) {
      router.push({ path: "/notes", query: { tag } });
    }
    return;
  }

  const wikilink = target.closest(".wikilink") as HTMLAnchorElement | null;
  if (wikilink) {
    e.preventDefault();
    const title = wikilink.dataset.title;
    if (title) {
      try {
        const note = await apiPost<Note>(
          "/api/notes/resolve-title",
          { title }
        );
        router.push(`/notes/${note.id}`);
      } catch {
        const { useToastStore } = await import("@/stores/toast");
        useToastStore().show(`Failed to resolve note "${title}"`, "error");
      }
    }
  }
}

function onTagClick(tag: string) {
  router.push({ path: "/tasks", query: { tag } });
}
</script>

<template>
  <main class="viewer">
    <p v-if="store.loading">Loading...</p>
    <template v-else-if="store.currentTask">
      <div class="toolbar">
        <router-link to="/tasks" class="btn-back">Back</router-link>
        <router-link
          :to="`/tasks/${store.currentTask.id}/edit`"
          class="btn-edit"
        >
          Edit
        </router-link>
        <button
          class="btn-convert"
          @click="convertToNote"
          :disabled="converting"
        >
          {{ converting ? "Converting..." : "Convert to Note" }}
        </button>
      </div>
      <h1>{{ store.currentTask.title || "Untitled" }}</h1>
      <p class="meta">
        Updated {{ relativeTime(store.currentTask.updated_at) }}
        &middot;
        Created {{ relativeTime(store.currentTask.created_at) }}
      </p>
      <div class="badges">
        <StatusBadge
          :status="store.currentTask.status!"
          clickable
          @click="cycleStatus"
        />
        <PriorityBadge :priority="store.currentTask.priority!" />
        <span
          v-if="store.currentTask.due_date"
          :class="['due-date', { overdue: isOverdue() }]"
        >
          Due: {{ store.currentTask.due_date }}
        </span>
      </div>
      <div class="tags" v-if="store.currentTask.tags.length">
        <TagPill
          v-for="tag in store.currentTask.tags"
          :key="tag"
          :tag="tag"
          @click="onTagClick"
        />
      </div>
      <div
        class="body prose"
        v-html="renderedBody"
        @click="onBodyClick"
      ></div>

      <div v-if="backlinks.length" class="backlinks">
        <h2>Backlinks</h2>
        <ul class="backlinks-list">
          <li v-for="link in backlinks" :key="`${link.type}-${link.id}`" class="backlink-item">
            <span class="backlink-type">{{ link.type }}</span>
            <router-link
              :to="`/${link.type === 'note' ? 'notes' : 'tasks'}/${link.id}`"
              class="backlink-link"
            >
              {{ link.title || "Untitled" }}
            </router-link>
          </li>
        </ul>
      </div>
    </template>
    <p v-else>Task not found.</p>
  </main>
</template>

<style scoped>
.viewer {
  max-width: 720px;
  margin: 2rem auto;
  padding: 0 1rem;
}
.toolbar {
  display: flex;
  gap: 0.75rem;
  margin-bottom: 1rem;
}
.btn-back,
.btn-edit {
  text-decoration: none;
  color: var(--color-primary);
}
.btn-convert {
  margin-left: auto;
  padding: 0.3rem 0.75rem;
  background: var(--color-bg-secondary);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  font-size: 0.85rem;
}
.btn-convert:hover {
  background: var(--color-primary);
  color: #fff;
  border-color: var(--color-primary);
}
.btn-convert:disabled {
  opacity: 0.6;
  cursor: default;
}
.meta {
  font-size: 0.85rem;
  color: var(--color-text-muted);
  margin: 0 0 0.75rem;
}
.badges {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}
.due-date {
  font-size: 0.85rem;
  color: var(--color-text-secondary);
}
.due-date.overdue {
  color: var(--color-overdue);
  font-weight: 600;
}
.tags {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1rem;
  flex-wrap: wrap;
}
.backlinks {
  margin-top: 2rem;
  border-top: 1px solid var(--color-border);
  padding-top: 1rem;
}
.backlinks h2 {
  font-size: 1.1rem;
  margin: 0 0 0.75rem;
}
.backlinks-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.backlink-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.backlink-link {
  color: var(--color-text);
  text-decoration: none;
  font-size: 0.95rem;
}
.backlink-link:hover {
  color: var(--color-primary);
}
.backlink-type {
  font-size: 0.75rem;
  text-transform: uppercase;
  color: var(--color-text-muted);
  background: var(--color-bg-secondary);
  padding: 0.1rem 0.4rem;
  border-radius: var(--radius-sm);
}
</style>
