<template>
  <div class="reader-view">
    <!-- Header -->
    <div class="reader-header">
      <router-link to="/" class="back-link">← Library</router-link>
      <div class="reader-title">
        <h1 class="book-title">{{ book?.title ?? 'Loading…' }}</h1>
      </div>
      <div class="page-nav">
        <button class="nav-btn" :disabled="currentPage <= 1" @click="goTo(currentPage - 1)">&#8592;</button>
        <form class="page-jump" @submit.prevent="onJump">
          <input
            v-model.number="jumpInput"
            type="number"
            :min="1"
            :max="book?.pageCount ?? 1"
            class="page-input"
          />
          <span class="page-sep">/ {{ book?.pageCount ?? '…' }}</span>
        </form>
        <button class="nav-btn" :disabled="!book || currentPage >= book.pageCount!" @click="goTo(currentPage + 1)">&#8594;</button>
      </div>
    </div>

    <!-- Content -->
    <div class="reader-body">
      <div v-if="loading" class="reader-loading">
        <div class="spinner spinner-dark" style="width:28px;height:28px;margin:0 auto 0.75rem;"></div>
        <p>Loading page {{ currentPage }}…</p>
      </div>

      <div v-else-if="error" class="reader-error card">
        <strong>Could not load page {{ currentPage }}</strong><br />
        {{ error }}
      </div>

      <div v-else class="reader-content card">
        <div class="markdown-body" v-html="renderedHtml"></div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, watch, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { marked } from 'marked'
import { api } from '@/services/api'
import { useBookStore } from '@/stores/bookStore'
import type { Book } from '@/stores/bookStore'

const route = useRoute()
const bookStore = useBookStore()

const bookId = route.params.id as string
const book = ref<Book | null>(null)
const currentPage = ref(1)
const jumpInput = ref(1)
const loading = ref(false)
const error = ref<string | null>(null)
const renderedHtml = ref('')

// Blob URLs created this session — revoked on next page load
let activeBlobUrls: string[] = []

onMounted(async () => {
  book.value = bookStore.books.find(b => b.id === bookId) ?? null
  if (!book.value) {
    try {
      const res = await api.get<Book>(`/books/${bookId}`)
      book.value = res.data
    } catch {
      error.value = 'Book not found.'
      return
    }
  }
  await loadPage(1)
})

watch(currentPage, (page) => {
  jumpInput.value = page
  loadPage(page)
})

async function goTo(page: number) {
  if (!book.value) return
  const clamped = Math.max(1, Math.min(page, book.value.pageCount ?? 1))
  if (clamped !== currentPage.value) {
    currentPage.value = clamped
  }
}

function onJump() {
  goTo(jumpInput.value)
}

async function loadPage(page: number) {
  loading.value = true
  error.value = null
  renderedHtml.value = ''

  // Revoke previous blob URLs to free memory
  activeBlobUrls.forEach(u => URL.revokeObjectURL(u))
  activeBlobUrls = []

  try {
    const res = await api.get<string>(`/books/${bookId}/pages/${page}/markdown`, {
      headers: { Accept: 'text/plain' },
      responseType: 'text'
    })
    const markdownText = res.data

    // Render markdown to HTML, then resolve image src via authenticated fetch
    let html = await marked.parse(markdownText) as string
    html = await resolveImages(html)
    renderedHtml.value = html
  } catch (e: any) {
    error.value = e.message ?? 'Failed to load page.'
  } finally {
    loading.value = false
  }
}

/**
 * Finds <img src="/api/v1/figures/..."> in the HTML, fetches each image
 * with the authenticated axios instance (which carries Basic auth headers),
 * and replaces the src with a temporary blob URL so the browser can display it.
 */
async function resolveImages(html: string): Promise<string> {
  const srcPattern = /src="(\/api\/v1\/figures\/[^"]+)"/g
  const matches = [...html.matchAll(srcPattern)]
  if (matches.length === 0) return html

  const unique = [...new Set(matches.map(m => m[1]))]
  const blobMap: Record<string, string> = {}

  await Promise.all(
    unique.map(async (src) => {
      try {
        const res = await api.get(src, { responseType: 'blob' })
        const blobUrl = URL.createObjectURL(res.data)
        activeBlobUrls.push(blobUrl)
        blobMap[src] = blobUrl
      } catch {
        // leave original src — browser will attempt (and likely fail silently)
      }
    })
  )

  return html.replace(/src="(\/api\/v1\/figures\/[^"]+)"/g, (_, src) =>
    blobMap[src] ? `src="${blobMap[src]}"` : `src="${src}"`
  )
}
</script>

<style scoped>
.reader-view {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 860px;
  margin: 0 auto;
}

.reader-header {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}

.back-link {
  color: #3182ce;
  text-decoration: none;
  font-size: 0.9rem;
  white-space: nowrap;
}
.back-link:hover { text-decoration: underline; }

.reader-title {
  flex: 1;
  min-width: 0;
}

.book-title {
  font-size: 1.1rem;
  font-weight: 600;
  color: #1a365d;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.page-nav {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.nav-btn {
  width: 2rem;
  height: 2rem;
  border: 1px solid #cbd5e0;
  border-radius: 6px;
  background: #fff;
  cursor: pointer;
  font-size: 1rem;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s;
}
.nav-btn:hover:not(:disabled) { background: #ebf8ff; border-color: #3182ce; }
.nav-btn:disabled { opacity: 0.4; cursor: not-allowed; }

.page-jump {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.page-input {
  width: 3.5rem;
  text-align: center;
  border: 1px solid #cbd5e0;
  border-radius: 6px;
  padding: 0.25rem 0.4rem;
  font-size: 0.9rem;
  color: #2d3748;
}
.page-input:focus { outline: none; border-color: #3182ce; }

.page-sep {
  font-size: 0.85rem;
  color: #718096;
  white-space: nowrap;
}

.reader-body {
  flex: 1;
}

.reader-loading {
  text-align: center;
  padding: 3rem;
  color: #718096;
}

.reader-error {
  padding: 1.25rem;
  background: #fff5f5;
  border: 1px solid #fed7d7;
  color: #742a2a;
  border-radius: 8px;
}

.reader-content {
  padding: 2rem;
}

/* Markdown rendering */
.markdown-body {
  font-size: 0.95rem;
  line-height: 1.75;
  color: #2d3748;
}

.markdown-body :deep(h1),
.markdown-body :deep(h2),
.markdown-body :deep(h3) {
  color: #1a365d;
  font-weight: 600;
  margin: 1.5rem 0 0.75rem;
}
.markdown-body :deep(h2) { font-size: 1.15rem; border-bottom: 1px solid #e2e8f0; padding-bottom: 0.4rem; }
.markdown-body :deep(h3) { font-size: 1rem; }

.markdown-body :deep(p) { margin: 0.75rem 0; }

.markdown-body :deep(img) {
  max-width: 100%;
  border-radius: 6px;
  display: block;
  margin: 1rem auto;
  box-shadow: 0 1px 4px rgba(0,0,0,0.12);
}

.markdown-body :deep(ul),
.markdown-body :deep(ol) {
  padding-left: 1.5rem;
  margin: 0.75rem 0;
}

.markdown-body :deep(code) {
  background: #f7fafc;
  border: 1px solid #e2e8f0;
  border-radius: 3px;
  padding: 0.1em 0.35em;
  font-size: 0.88em;
}

.markdown-body :deep(blockquote) {
  border-left: 3px solid #3182ce;
  padding-left: 1rem;
  color: #4a5568;
  margin: 0.75rem 0;
}

.markdown-body :deep(table) {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9em;
  margin: 1rem 0;
}
.markdown-body :deep(th),
.markdown-body :deep(td) {
  border: 1px solid #e2e8f0;
  padding: 0.4rem 0.75rem;
  text-align: left;
}
.markdown-body :deep(th) { background: #f7fafc; font-weight: 600; }
</style>
