<script setup lang="ts">
import type { LedgerEntry } from "@cents/domain";
import {
  BookOpen,
  ChevronDown,
  CircleUserRound,
  Copy,
  LoaderCircle,
  MoreHorizontal,
  Search,
  Settings,
  Share2,
  X,
} from "@lucide/vue";
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
import { useRouter } from "vue-router";
import QuickEntryHost from "../components/QuickEntryHost.vue";
import { apiRequest, ApiError } from "../data/api";
import { findCategory, findCategoryPath } from "../data/categories";
import { useEntryStore } from "../stores/entries";
import { useLedgerStore } from "../stores/ledgers";

const ENTRY_PAGE_SIZE = 50;
const store = useEntryStore();
const ledgerStore = useLedgerStore();
const router = useRouter();
const moreMenuOpen = ref(false);
const participantOpen = ref(false);
const shareOpen = ref(false);
const shareFeedback = ref("");
const invitationLink = ref("");
const generatingInvitation = ref(false);
const visibleCount = ref(ENTRY_PAGE_SIZE);
const ledgerContent = ref<HTMLElement | null>(null);
const loadMoreTrigger = ref<HTMLElement | null>(null);
let loadMoreObserver: IntersectionObserver | null = null;

const monthlyEntries = computed(() => {
  const current = new Date();
  return store.entries.filter((entry) => {
    const occurredAt = new Date(entry.occurredAt);
    return entry.ledgerId === ledgerStore.currentLedgerId
      && occurredAt.getFullYear() === current.getFullYear()
      && occurredAt.getMonth() === current.getMonth();
  });
});

const totals = computed(() => {
  return monthlyEntries.value.reduce(
    (result, entry) => {
      if (entry.type === "income") result.income += entry.baseAmount;
      else result.expense += entry.baseAmount;
      return result;
    },
    { income: 0, expense: 0 },
  );
});

const visibleMonthlyEntries = computed(() => monthlyEntries.value.slice(0, visibleCount.value));
const hasMoreEntries = computed(() => visibleCount.value < monthlyEntries.value.length);

const dailyTotals = computed(() => {
  const summaries = new Map<string, { income: number; expense: number }>();
  for (const entry of monthlyEntries.value) {
    const key = localDateKey(entry.occurredAt);
    const summary = summaries.get(key) ?? { income: 0, expense: 0 };
    if (entry.type === "income") summary.income += entry.baseAmount;
    else summary.expense += entry.baseAmount;
    summaries.set(key, summary);
  }
  return summaries;
});

const groupedEntries = computed(() => {
  const groups = new Map<string, LedgerEntry[]>();
  for (const entry of visibleMonthlyEntries.value) {
    const key = localDateKey(entry.occurredAt);
    const list = groups.get(key) ?? [];
    list.push(entry);
    groups.set(key, list);
  }
  return Array.from(groups, ([date, entries]) => ({
    date,
    entries,
    income: dailyTotals.value.get(date)?.income ?? 0,
    expense: dailyTotals.value.get(date)?.expense ?? 0,
  }));
});

const monthTitle = computed(() => {
  const date = new Date();
  return `${date.getFullYear()}年 ${date.getMonth() + 1}月`;
});
const monthListTitle = computed(() => {
  const date = new Date();
  return `${date.getFullYear()}年${date.getMonth() + 1}月流水`;
});

onMounted(async () => {
  await Promise.all([store.loadEntries(), ledgerStore.loadLedgers()]);
});

watch(monthlyEntries, () => {
  visibleCount.value = ENTRY_PAGE_SIZE;
});

watch([ledgerContent, loadMoreTrigger], ([root, trigger]) => {
  loadMoreObserver?.disconnect();
  loadMoreObserver = null;
  if (!root || !trigger) return;
  loadMoreObserver = new IntersectionObserver(
    ([entry]) => {
      if (entry?.isIntersecting) {
        visibleCount.value = Math.min(visibleCount.value + ENTRY_PAGE_SIZE, monthlyEntries.value.length);
      }
    },
    { root, rootMargin: "160px 0px" },
  );
  loadMoreObserver.observe(trigger);
});

onBeforeUnmount(() => loadMoreObserver?.disconnect());

function localDateKey(value: string) {
  const date = new Date(value);
  return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
}

function dateHeading(value: string) {
  const date = new Date(`${value}T00:00:00`);
  const today = new Date();
  const yesterday = new Date(today);
  yesterday.setDate(today.getDate() - 1);
  const label = localDateKey(today.toISOString()) === value
    ? "今天"
    : localDateKey(yesterday.toISOString()) === value
      ? "昨天"
      : `${date.getMonth() + 1}月${date.getDate()}日`;
  const weekday = new Intl.DateTimeFormat("zh-CN", { weekday: "short" }).format(date);
  return `${label} · ${weekday}`;
}

function formatMoney(value: number) {
  return (value / 100).toLocaleString("zh-CN", {
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  });
}

function formatTime(value: string) {
  return new Intl.DateTimeFormat("zh-CN", { hour: "2-digit", minute: "2-digit", hour12: false }).format(new Date(value));
}

function categoryLabel(entry: LedgerEntry) {
  const path = findCategoryPath(entry.type, entry.categoryId);
  return path.map((item) => item.label).join(" · ") || (entry.type === "expense" ? "支出" : "收入");
}

function openParticipantSheet() {
  participantOpen.value = true;
  void ledgerStore.loadCurrentMembers();
}

async function openShareSheet() {
  moreMenuOpen.value = false;
  shareFeedback.value = "正在生成邀请链接";
  shareOpen.value = true;
  await ensureInvitation();
}

function openLedgerSettings() {
  moreMenuOpen.value = false;
  router.push("/ledger/settings");
}

async function ensureInvitation() {
  if (invitationLink.value || generatingInvitation.value || !ledgerStore.currentLedgerId) return invitationLink.value;
  generatingInvitation.value = true;
  try {
    const result = await apiRequest<{ invitation: { url: string; expiresAt: string } }>(
      `/api/ledgers/${encodeURIComponent(ledgerStore.currentLedgerId)}/invitations`,
      { method: "POST" },
    );
    invitationLink.value = result.invitation.url;
    shareFeedback.value = "邀请链接将在 7 天后失效";
  } catch (error) {
    shareFeedback.value = error instanceof ApiError ? error.message : "邀请链接生成失败";
  } finally {
    generatingInvitation.value = false;
  }
  return invitationLink.value;
}

async function copyInvitation() {
  const link = await ensureInvitation();
  if (!link) return;
  if (navigator.clipboard?.writeText) await navigator.clipboard.writeText(link);
  else {
    const input = document.createElement("textarea");
    input.value = link;
    document.body.append(input);
    input.select();
    document.execCommand("copy");
    input.remove();
  }
  shareFeedback.value = "邀请链接已复制";
}

async function shareLedger() {
  const url = await ensureInvitation();
  if (!url) return;
  const data = {
    title: ledgerStore.currentLedger?.name ?? "账本",
    text: `邀请你加入我的${ledgerStore.currentLedger?.name ?? "账本"}`,
    url,
  };
  if (!navigator.share) {
    await copyInvitation();
    return;
  }
  try {
    await navigator.share(data);
    shareFeedback.value = "已打开系统分享";
  } catch (error) {
    if (error instanceof DOMException && error.name === "AbortError") return;
    shareFeedback.value = "分享失败，请复制邀请链接";
  }
}

</script>

<template>
  <main class="app-shell">
    <header class="ledger-header">
      <div class="top-actions">
        <button class="participant-avatars" type="button" aria-label="查看账本参与者" title="账本参与者" @click="openParticipantSheet">
          <span v-for="member in ledgerStore.currentMembers.slice(0, 3)" :key="member.id" class="participant-avatar" :class="member.role">
            {{ member.name.slice(0, 1) }}
          </span>
        </button>
        <button class="ledger-switcher" type="button" @click="router.push('/ledgers')">
          <span>{{ ledgerStore.currentLedger?.name ?? "选择账本" }}</span>
          <ChevronDown :size="16" />
        </button>
        <button v-if="ledgerStore.currentRole === 'owner'" class="icon-button header-icon" type="button" aria-label="更多" title="更多" @click.stop="moreMenuOpen = true">
          <MoreHorizontal :size="21" />
        </button>
      </div>

      <Transition name="more-menu">
        <div v-if="moreMenuOpen" class="ledger-more-layer">
          <button class="ledger-more-scrim" type="button" aria-label="关闭更多菜单" @click="moreMenuOpen = false"></button>
          <div class="ledger-more-menu" role="menu">
            <button type="button" role="menuitem" @click="openLedgerSettings"><Settings :size="18" />设置</button>
            <button type="button" role="menuitem" @click="openShareSheet"><Share2 :size="18" />分享</button>
          </div>
        </div>
      </Transition>

      <button class="month-switcher" type="button">
        {{ monthTitle }}
        <ChevronDown :size="14" />
      </button>

      <section class="monthly-summary" aria-label="本月收支">
        <div class="summary-main">
          <span>本月结余</span>
          <strong>¥ {{ formatMoney(totals.income - totals.expense) }}</strong>
        </div>
        <div class="summary-stat income-stat">
          <span>收入</span>
          <strong>{{ formatMoney(totals.income) }}</strong>
        </div>
        <div class="summary-stat expense-stat">
          <span>支出</span>
          <strong>{{ formatMoney(totals.expense) }}</strong>
        </div>
      </section>
    </header>

    <section ref="ledgerContent" class="ledger-content" aria-label="账本流水">
      <div class="list-toolbar">
        <div class="list-toolbar-copy">
          <div class="list-toolbar-heading">
            <strong>{{ monthListTitle }}</strong>
            <span>{{ monthlyEntries.length }} 笔</span>
          </div>
          <div class="list-toolbar-totals">
            <span class="income">收入 {{ formatMoney(totals.income) }}</span>
            <span class="expense">支出 {{ formatMoney(totals.expense) }}</span>
          </div>
        </div>
        <button class="icon-button list-action" type="button" aria-label="搜索流水" title="搜索流水">
          <Search :size="19" />
        </button>
      </div>

      <section v-for="group in groupedEntries" :key="group.date" class="day-group">
        <div class="day-group-heading">
          <h2>{{ dateHeading(group.date) }}</h2>
          <div class="day-summary">
            <span class="income">收 {{ formatMoney(group.income) }}</span>
            <span class="expense">支 {{ formatMoney(group.expense) }}</span>
          </div>
        </div>
        <RouterLink v-for="entry in group.entries" :key="entry.id" class="entry-row" :to="`/entries/${entry.id}`">
          <div
            class="entry-category-icon"
            :style="{
              color: findCategory(entry.categoryId)?.color ?? (entry.type === 'expense' ? '#ef5b3f' : '#16966f'),
              background: findCategory(entry.categoryId)?.tint ?? (entry.type === 'expense' ? '#fff0ec' : '#e9faf4'),
            }"
          >
            <component :is="findCategory(entry.categoryId)?.icon ?? BookOpen" :size="21" />
          </div>
          <div class="entry-copy">
            <strong>{{ entry.note || categoryLabel(entry) }}</strong>
            <div class="entry-subline">
              <span class="entry-recorder"><CircleUserRound :size="12" />{{ ledgerStore.memberName(entry.createdBy) }}</span>
              <span class="entry-category-label">{{ categoryLabel(entry) }}</span>
            </div>
          </div>
          <div class="entry-value">
            <strong :class="entry.type">
              {{ entry.type === "expense" ? "−" : "+" }}{{ formatMoney(entry.baseAmount) }}
            </strong>
            <span>{{ formatTime(entry.occurredAt) }}</span>
          </div>
        </RouterLink>
      </section>

      <div v-if="hasMoreEntries" ref="loadMoreTrigger" class="load-more-sentinel" aria-live="polite">
        <LoaderCircle :size="17" />
        正在加载更多流水
      </div>

      <div v-if="!monthlyEntries.length" class="empty-state">
        <BookOpen :size="26" />
        <strong>还没有流水</strong>
        <span>从第一笔开始记录</span>
      </div>
    </section>

    <Transition name="share-sheet">
      <div v-if="participantOpen" class="share-sheet-layer">
        <button class="share-sheet-scrim" type="button" aria-label="关闭参与者列表" @click="participantOpen = false"></button>
        <section class="ledger-share-sheet participant-sheet" aria-label="账本参与者">
          <div class="share-sheet-handle"></div>
          <header>
            <div>
              <strong>{{ ledgerStore.currentLedger?.name ?? "账本" }}</strong>
              <span>{{ ledgerStore.currentMembers.length }} 位参与者</span>
            </div>
            <button type="button" aria-label="关闭" title="关闭" @click="participantOpen = false"><X :size="20" /></button>
          </header>

          <div class="share-member-list" aria-label="当前参与者">
            <div v-for="member in ledgerStore.currentMembers" :key="member.id" class="share-member-row">
              <span class="share-member-avatar" :class="member.role">{{ member.name.slice(0, 1) }}</span>
              <span><strong>{{ member.name }}</strong><small>{{ member.role === "owner" ? "拥有者" : "成员" }}</small></span>
            </div>
          </div>

        </section>
      </div>
    </Transition>

    <Transition name="share-sheet">
      <div v-if="shareOpen" class="share-sheet-layer">
        <button class="share-sheet-scrim" type="button" aria-label="关闭账本分享" @click="shareOpen = false"></button>
        <section class="ledger-share-sheet compact-share-sheet" aria-label="账本分享">
          <div class="share-sheet-handle"></div>
          <header>
            <div>
              <strong>分享 {{ ledgerStore.currentLedger?.name ?? "账本" }}</strong>
              <span>邀请成员加入当前账本</span>
            </div>
            <button type="button" aria-label="关闭" title="关闭" @click="shareOpen = false"><X :size="20" /></button>
          </header>
          <div class="share-actions">
            <button type="button" class="copy-invite-action" :disabled="generatingInvitation" @click="copyInvitation"><Copy :size="18" />复制链接</button>
            <button type="button" class="system-share-action" :disabled="generatingInvitation" @click="shareLedger"><Share2 :size="18" />分享账本</button>
          </div>
          <p class="share-feedback" role="status">{{ shareFeedback || "通过邀请链接加入此账本" }}</p>
        </section>
      </div>
    </Transition>

    <QuickEntryHost />
  </main>
</template>
