<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue';
import { useRouter } from 'vue-router';
import { useTokenStore } from '@/stores/token';
import DateTimePicker, { TabType } from '@/components/DateTimePicker.vue';
import CalendarGrid from '@/components/calendar/CalendarGrid.vue';
import ScheduleList from '@/components/calendar/ScheduleList.vue';
import OwnedItem from '@/components/calendar/OwnedItem.vue';
import SharedItem from '@/components/calendar/SharedItem.vue';
import type { CalendarCell, ScheduleEventItem } from '@/components/calendar/types';
import {
  getFull as apiCalendarGetFull,
  deserializeDescription,
  type CalendarRow,
} from '@/api/calendar';
import {
  getFullOwn as apiCollectionGetFullOwn,
  getShared as apiCollectionGetShared,
  type CollectionRow,
  type SharedCollectionRow,
} from '@/api/collection';
import { DAY1_SPAN, dayOfWeek, resolveLoopRules2Event, resolveLoopRules4Text } from '@/utils/datetime';
import { universalGetMonth, WEEK_NAMES } from '@/utils/i18n';
import { getWeekday } from '@/utils/utils';

enum CalendarTab {
  Calendar = 1,
  Collection = 2,
  Display = 3,
}

const token = useTokenStore();
const router = useRouter();

// tab control state
const activeTab = ref<CalendarTab>(CalendarTab.Calendar);

// the month currently being viewed (drives the visible 6-week window)
const viewedMonth = ref<Date>(new Date());

// raw events for the current window (Refresh phase)
const rawEvents = ref<CalendarRow[]>([]);

// collection sidebar data + per-collection visibility toggles
const ownedCollections = ref<CollectionRow[]>([]);
const sharedCollections = ref<SharedCollectionRow[]>([]);
const ownedVisible = ref<Map<string, boolean>>(new Map());
const sharedVisible = ref<Map<string, boolean>>(new Map());

const picker = ref<InstanceType<typeof DateTimePicker>>();

// the browser timezone offset in minutes (matches legacy -(getTimezoneOffset))
const myTimezone = computed<number>(() => -(new Date().getTimezoneOffset()));

// set of owned collection uuids (stable across visibility toggles)
const ownedUuids = computed<Set<string>>(() => new Set(ownedCollections.value.map(c => c[0])));

const monthLabel = computed<string>(() => {
  return `${viewedMonth.value.getFullYear()} - ${universalGetMonth(viewedMonth.value.getMonth())}`;
})

// window covering the 6x7 grid: from the Monday of the week containing the 1st
interface WindowRange {
  startTs: number
  endTs: number
  year: number
  month1: number
}

const windowRange = computed<WindowRange>(() => {
  const date = viewedMonth.value
  const year = date.getFullYear()
  const month1 = date.getMonth() + 1
  // The legacy picker stored only year+month for this slot (mode=month), and
  // its Get() defaulted day/hour/minute to 1/0/0/0. Reproduce that here so the
  // visible 6-week window aligns to the first of the month.
  const firstOfMonth = new Date(year, date.getMonth(), 1, 0, 0, 0, 0)
  const week = dayOfWeek(year, month1, 1)
  const startTs = Math.floor(firstOfMonth.getTime() / 60000) - week * DAY1_SPAN
  const endTs = startTs + DAY1_SPAN * 6 * 7 - 1
  return { startTs, endTs, year, month1 }
})

// Analyse phase: build the 42 cells and expand every event (loop expansion +
// multi-day splitting). Deliberately does NOT read the visibility maps so it
// stays cached across visibility toggles.
const expandedCells = computed<CalendarCell[]>(() => {
  const { startTs, endTs, month1: gottenMonth } = windowRange.value
  const owned = ownedUuids.value
  const myTz = myTimezone.value

  // build the 42 day cells
  const cells: CalendarCell[] = []
  const iter = new Date(startTs * 60000)
  for (let i = 0; i < 42; i++) {
    cells.push({
      month: iter.getMonth() + 1,
      day: iter.getDate(),
      dayOfWeek: getWeekday(iter) + 1,
      isCurrentMonth: (iter.getMonth() + 1) === gottenMonth,
      events: [],
    })
    iter.setTime(iter.getTime() + DAY1_SPAN * 60000)
  }

  // expand each event into its occurrences and split across days
  for (const item of rawEvents.value) {
    const desc = deserializeDescription(item[3])
    const minStartTs = startTs - (item[6] - item[5])
    const result = resolveLoopRules2Event(
      item[8],
      item[9] < minStartTs ? minStartTs : item[9],
      Math.min(item[10], endTs),
      item[5],
      item[6],
      item[7],
      startTs,
    )
    if (typeof result === 'undefined') continue
    const loopText = resolveLoopRules4Text(item[8], item[5], item[7])
    const tzWarn = myTz !== item[7]
    // lock icon = event is in one of MY collections (belongTo). (Legacy
    // mistakenly keyed this by the event uuid; fixed per migration decision.)
    const isLocked = owned.has(item[1])

    for (const range of result) {
      const it0 = range[0]
      const it1 = range[1]
      const eventDateTime = new Date(it0 * 60000)
      let count = Math.floor((it0 - startTs) / DAY1_SPAN)
      let exitFlag = false
      while (count < 42) {
        const eventItem: ScheduleEventItem = {
          uuid: item[0],
          belongTo: item[1],
          title: item[2],
          description: desc.description,
          color: desc.color,
          isVisible: true,
          isLocked,
          loopText,
          timezoneWarning: tzWarn,
          start: eventDateTime.toLocaleTimeString(),
          end: '',
        }
        eventDateTime.setHours(23, 59, 0, 0)
        if (it1 <= Math.floor(eventDateTime.getTime() / 60000)) {
          exitFlag = true
          eventDateTime.setTime(it1 * 60000)
        }
        eventItem.end = eventDateTime.toLocaleTimeString()
        cells[count]!.events.push(eventItem)
        if (exitFlag) break
        eventDateTime.setMinutes(eventDateTime.getMinutes() + 1, 0, 0)
        count++
      }
    }
  }

  return cells
})

// Render phase: apply per-collection visibility to each event.
const displayCache = computed<CalendarCell[]>(() => {
  const owned = ownedVisible.value
  const shared = sharedVisible.value
  return expandedCells.value.map(cell => ({
    ...cell,
    events: cell.events.map(ev => ({
      ...ev,
      isVisible: (owned.get(ev.belongTo) ?? false) || (shared.get(ev.belongTo) ?? false),
    })),
  }))
})

const fetchEvents = async () => {
  const { startTs, endTs } = windowRange.value
  const rv = await apiCalendarGetFull(token.currentToken, startTs, endTs)
  rawEvents.value = typeof rv === 'undefined' ? [] : rv
}

const refreshCollections = async () => {
  const ownedRv = await apiCollectionGetFullOwn(token.currentToken)
  ownedCollections.value = ownedRv ?? []
  const ownedMap = new Map<string, boolean>()
  for (const c of ownedCollections.value) ownedMap.set(c[0], true)
  ownedVisible.value = ownedMap

  const sharedRv = await apiCollectionGetShared(token.currentToken)
  sharedCollections.value = sharedRv ?? []
  const sharedMap = new Map<string, boolean>()
  for (const c of sharedCollections.value) sharedMap.set(c[0], true)
  sharedVisible.value = sharedMap
}

const toggleOwned = (uuid: string) => {
  const cur = ownedVisible.value.get(uuid) ?? false
  ownedVisible.value.set(uuid, !cur)
}

const toggleShared = (uuid: string) => {
  const cur = sharedVisible.value.get(uuid) ?? false
  sharedVisible.value.set(uuid, !cur)
}

const openMonthPicker = () => {
  picker.value?.modal(viewedMonth.value, TabType.Month)
}

const onPickerConfirm = (d: Date) => {
  viewedMonth.value = d
}

const goToday = () => {
  viewedMonth.value = new Date()
}

const prevMonth = (): void => {
  // TODO: prev/next month navigation not implemented (legacy left these buttons unbound).
}

const nextMonth = (): void => {
  // TODO: prev/next month navigation not implemented (legacy left these buttons unbound).
}

const goAddEvent = () => {
  router.push({ name: 'CalendarEventAdd' })
}

const onEventEdit = (uuid: string) => {
  router.push({ name: 'CalendarEventUpdate', params: { uuid } })
}

watch(viewedMonth, () => {
  fetchEvents()
})

onMounted(async () => {
  await refreshCollections()
  await fetchEvents()
})
</script>

<template>
  <div class="container" style="margin-top: 20px;">
    <div class="tabs">
      <ul>
        <li :class="{ 'is-active': activeTab === CalendarTab.Calendar }" @click="activeTab = CalendarTab.Calendar">
          <a>Calendar</a>
        </li>
        <li :class="{ 'is-active': activeTab === CalendarTab.Collection }"
          @click="activeTab = CalendarTab.Collection"><a>Collection</a></li>
        <li :class="{ 'is-active': activeTab === CalendarTab.Display }" @click="activeTab = CalendarTab.Display"><a>Display</a></li>
      </ul>
    </div>
  </div>

  <div v-show="activeTab === CalendarTab.Calendar" class="container" style="margin-top: 20px;">
    <nav class="level is-mobile">
      <div class="level-left">
        <div class="level-item control">
          <a class="button" @click="prevMonth">
            <span class="icon is-small"><font-awesome-icon icon="fas fa-chevron-circle-left"></font-awesome-icon></span>
          </a>
        </div>
      </div>

      <div class="level-item control">
        <a class="button" @click="openMonthPicker">
          <span>{{ monthLabel }}</span>
        </a>
      </div>

      <div class="level-right">
        <div class="level-item control">
          <a class="button" @click="nextMonth">
            <span class="icon is-small"><font-awesome-icon icon="fas fa-chevron-circle-right"></font-awesome-icon></span>
          </a>
        </div>
      </div>
    </nav>

    <nav class="level is-mobile">
      <div class="level-item control">
        <a class="button is-info" @click="goToday">Today</a>
      </div>
      <div class="level-item control">
        <a class="button is-primary" @click="goAddEvent">Add...</a>
      </div>
    </nav>

    <CalendarGrid :cells="displayCache" />

    <div class="container" style="padding: 1.25rem; display: flex; flex-flow: column; margin-top: 1.25rem;">
      <h1 class="title">Schedule</h1>
      <ScheduleList :cells="displayCache" @edit="onEventEdit" />
    </div>
  </div>

  <div v-show="activeTab === CalendarTab.Collection" class="container" style="margin-top: 20px;">
    <div class="control" style="margin: 0.75rem;" @click="refreshCollections">
      <a class="button is-primary">
        <span class="icon is-small"><font-awesome-icon icon="fas fa-sync"></font-awesome-icon></span>
      </a>
    </div>
    <h1 class="title">My collections</h1>
    <div style="display: flex; flex-flow: column; margin-top: 1.25rem; margin-bottom: 1.25rem;">
      <div v-for="c in ownedCollections" :key="c[0]"
        style="display: flex; flex-flow: column; margin-top: 1.25rem;">
        <OwnedItem :name="c[1]" :is-visible="ownedVisible.get(c[0]) ?? false" @toggle="toggleOwned(c[0])" />
      </div>
    </div>
    <h1 class="title">Shared collections</h1>
    <div style="display: flex; flex-flow: column; margin-top: 1.25rem; margin-bottom: 1.25rem;">
      <div v-for="c in sharedCollections" :key="c[0]"
        style="display: flex; flex-flow: column; margin-top: 1.25rem;">
        <SharedItem :name="c[1]" :username="c[2]" :is-visible="sharedVisible.get(c[0]) ?? false"
          @toggle="toggleShared(c[0])" />
      </div>
    </div>
  </div>

  <div v-show="activeTab === CalendarTab.Display" class="container" style="margin-top: 20px;">
    <div class="field">
      <label class="label">The first day of week</label>
      <div class="control">
        <div class="select">
          <select>
            <option v-for="(w, i) in WEEK_NAMES" :key="i">{{ w }}</option>
          </select>
        </div>
      </div>
    </div>
    <div class="field">
      <label class="label">Sub-Calendar</label>
      <div class="control">
        <div class="select">
          <select>
            <option>None</option>
            <option>Chinese Lunisolar Calendar</option>
          </select>
        </div>
      </div>
    </div>
  </div>

  <DateTimePicker ref="picker" @confirm="onPickerConfirm" />
</template>

<style scoped></style>
