<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import {
  getBriefingFeeds,
  postRssReaction,
  deleteRssReaction,
  getNewsItems,
  openArticleInChat,
  type BriefingFeed,
} from '@/api/client'
import type { NewsItem } from '@/types/news'

const router = useRouter()

const LIMIT = 40

const items = ref<NewsItem[]>([])
const offset = ref(0)
const hasMore = ref(true)
const loading = ref(false)
const feeds = ref<BriefingFeed[]>([])
const selectedFeedId = ref<number | null>(null)

// Reactions map: item id → current reaction
const reactions = ref<Record<number, 'up' | 'down' | null>>({})
// Track which items are currently being opened in chat
const openingChat = ref<Set<number>>(new Set())

async function loadMore() {
  if (loading.value || !hasMore.value) return
  loading.value = true
  try {
    const data = await getNewsItems({
      days: 90,
      limit: LIMIT,
      offset: offset.value,
      feed_id: selectedFeedId.value,
    })
    for (const item of data.items) {
      if (reactions.value[item.id] === undefined) {
        reactions.value[item.id] = item.reaction
      }
    }
    items.value = [...items.value, ...data.items]
    offset.value += data.items.length
    hasMore.value = data.items.length === LIMIT
  } finally {
    loading.value = false
  }
}

function onFeedChange() {
  items.value = []
  offset.value = 0
  hasMore.value = true
  reactions.value = {}
  loadMore()
}

async function handleReaction(itemId: number, reaction: 'up' | 'down') {
  const current = reactions.value[itemId]
  reactions.value[itemId] = current === reaction ? null : reaction
  try {
    if (current === reaction) {
      await deleteRssReaction(itemId)
    } else {
      await postRssReaction(itemId, reaction)
    }
  } catch {
    reactions.value[itemId] = current ?? null
  }
}

function formatRelativeDate(iso: string | null): string {
  if (!iso) return ''
  const d = new Date(iso)
  const now = new Date()
  const diffH = (now.getTime() - d.getTime()) / 3_600_000
  if (diffH < 1) return 'Just now'
  if (diffH < 24) return `${Math.round(diffH)}h ago`
  if (diffH < 48) return 'Yesterday'
  const days = Math.floor(diffH / 24)
  if (days < 7) return `${days}d ago`
  return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })
}

async function openInChat(itemId: number) {
  if (openingChat.value.has(itemId)) return
  openingChat.value.add(itemId)
  try {
    const result = await openArticleInChat(itemId)
    router.push(`/chat/${result.conversation_id}`)
  } catch {
    // silently fail — button returns to enabled state
  } finally {
    openingChat.value.delete(itemId)
  }
}

onMounted(async () => {
  feeds.value = await getBriefingFeeds().catch(() => [])
  await loadMore()
})
</script>

<template>
  <div class="news-root">
    <div class="news-header">
      <div class="news-header-left">
        <h1 class="news-title">News</h1>
        <span class="news-subtitle">Last 90 days</span>
      </div>
      <div class="news-header-right">
        <select
          v-model="selectedFeedId"
          class="feed-select"
          @change="onFeedChange"
        >
          <option :value="null">All feeds</option>
          <option v-for="feed in feeds" :key="feed.id" :value="feed.id">
            {{ feed.title }}
          </option>
        </select>
      </div>
    </div>

    <div class="news-list">
      <div v-if="!items.length && !loading" class="news-empty">
        No articles found for the selected feed.
      </div>

      <div
        v-for="item in items"
        :key="item.id"
        class="news-card"
      >
        <div class="news-card-meta">
          <span class="news-source">{{ item.source }}</span>
          <span v-if="item.published_at" class="news-date">{{ formatRelativeDate(item.published_at) }}</span>
        </div>
        <a
          v-if="item.url"
          :href="item.url"
          target="_blank"
          rel="noopener noreferrer"
          class="news-card-title"
        >{{ item.title }}</a>
        <p v-else class="news-card-title news-card-title--plain">{{ item.title }}</p>
        <p v-if="item.snippet" class="news-snippet">{{ item.snippet }}</p>
        <div v-if="item.topics?.length" class="news-topics">
          <span v-for="topic in item.topics" :key="topic" class="news-topic">{{ topic }}</span>
        </div>
        <div class="news-reactions">
          <button
            class="reaction-btn"
            :class="{ active: reactions[item.id] === 'up' }"
            @click="handleReaction(item.id, 'up')"
            title="Interested"
          >👍</button>
          <button
            class="reaction-btn"
            :class="{ active: reactions[item.id] === 'down' }"
            @click="handleReaction(item.id, 'down')"
            title="Not interested"
          >👎</button>
          <button
            class="reaction-btn open-chat-btn"
            :class="{ busy: openingChat.has(item.id) }"
            :disabled="openingChat.has(item.id)"
            @click="openInChat(item.id)"
            title="Discuss in chat"
          >{{ openingChat.has(item.id) ? '…' : '💬' }}</button>
        </div>
      </div>

      <div class="news-footer">
        <button
          v-if="hasMore"
          class="btn-load-more"
          @click="loadMore"
          :disabled="loading"
        >{{ loading ? 'Loading…' : 'Load more' }}</button>
        <div v-if="loading && !items.length" class="news-loading">Loading…</div>
        <p v-if="!hasMore && items.length" class="news-end">All articles loaded</p>
      </div>
    </div>
  </div>
</template>

<style scoped>
.news-root {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  overflow-y: auto;
}

.news-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.25rem 1.5rem 1rem;
  border-bottom: 1px solid var(--color-border);
  flex-shrink: 0;
  flex-wrap: wrap;
  gap: 0.75rem;
}

.news-header-left {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
}

.news-title {
  font-family: 'Fraunces', Georgia, serif;
  font-size: 1.3rem;
  font-weight: 700;
  margin: 0;
  color: var(--color-text);
}

.news-subtitle {
  font-size: 0.82rem;
  color: var(--color-text-muted);
}

.news-header-right {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.feed-select {
  padding: 0.35rem 0.6rem;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  background: var(--color-bg-card);
  color: var(--color-text);
  font-size: 0.82rem;
  cursor: pointer;
  font-family: inherit;
}

.news-list {
  padding: 1rem 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  max-width: 860px;
  width: 100%;
  margin: 0 auto;
}

.news-empty,
.news-loading {
  text-align: center;
  padding: 3rem 1rem;
  color: var(--color-text-muted);
  font-size: 0.9rem;
}

.news-card {
  background: var(--color-bg-card);
  border: 1px solid var(--color-border);
  border-radius: 10px;
  padding: 0.85rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

.news-card-meta {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.news-source {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-primary);
}

.news-date {
  font-size: 0.72rem;
  color: var(--color-text-muted);
}

.news-card-title {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--color-text);
  line-height: 1.4;
  text-decoration: none;
  margin: 0;
}

a.news-card-title:hover {
  text-decoration: underline;
  color: var(--color-primary);
}

.news-snippet {
  font-size: 0.82rem;
  color: var(--color-text-muted);
  line-height: 1.5;
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.news-topics {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin-top: 0.1rem;
}

.news-topic {
  font-size: 0.68rem;
  padding: 0.15rem 0.5rem;
  background: color-mix(in srgb, var(--color-primary) 10%, transparent);
  color: var(--color-primary);
  border-radius: 99px;
  font-weight: 500;
}

.news-reactions {
  display: flex;
  gap: 0.3rem;
  margin-top: 0.2rem;
}

.reaction-btn {
  background: none;
  border: 1px solid var(--color-border);
  border-radius: 6px;
  padding: 0.1rem 0.4rem;
  cursor: pointer;
  font-size: 0.82rem;
  line-height: 1.4;
  opacity: 0.55;
  transition: opacity 0.15s, border-color 0.15s;
}

.reaction-btn:hover {
  opacity: 1;
  border-color: var(--color-primary);
}

.reaction-btn.active {
  opacity: 1;
  border-color: var(--color-primary);
  background: color-mix(in srgb, var(--color-primary) 12%, transparent);
}

.open-chat-btn {
  margin-left: auto;
}

.open-chat-btn.busy {
  opacity: 0.4;
  cursor: wait;
}

.news-footer {
  display: flex;
  justify-content: center;
  padding: 1rem 0 0.5rem;
}

.btn-load-more {
  padding: 0.5rem 1.5rem;
  border: 1px solid var(--color-border);
  border-radius: 8px;
  background: var(--color-bg-card);
  color: var(--color-text-muted);
  font-size: 0.85rem;
  cursor: pointer;
  font-family: inherit;
  transition: all 0.15s;
}

.btn-load-more:hover:not(:disabled) {
  border-color: var(--color-primary);
  color: var(--color-primary);
}

.btn-load-more:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.news-end {
  font-size: 0.8rem;
  color: var(--color-text-muted);
  margin: 0;
}
</style>
