"use client"

import { create } from "zustand"
import { persist } from "zustand/middleware"
import { debouncedPersistJSONStorage } from "@/lib/stores/debounced-json-storage"
import {
  DEFAULT_AGENDA_USER_SETTINGS,
} from "@/lib/agenda/agenda-settings-defaults"
import {
  migrateAutoImportIdentityKeys,
  normalizeAutoImportInvitationSources,
  normalizeInvitationImportExclusions,
} from "@/lib/agenda/agenda-destination-identities"
import type {
  AgendaAutoImportSource,
  AgendaCalendarView,
  AgendaDurationStep,
  AgendaExternalCalendar,
  AgendaInvitationExclusion,
  AgendaTimeFormat,
  AgendaUserSettings,
  AgendaVideoProvider,
  AgendaWeekStart,
} from "@/lib/agenda/agenda-settings-types"
import {
  EXTERNAL_CALENDAR_ID_PREFIX,
  isReservedAgendaViewName,
} from "@/lib/agenda/agenda-calendar-visibility"
import type { AgendaView } from "./agenda-url.ts"

function newAgendaId(prefix: string): string {
  return `${prefix}${crypto.randomUUID()}`
}

type AgendaSettingsState = AgendaUserSettings & {
  quickSettingsOpen: boolean
  hiddenCalendarIds: string[]
  lastView: AgendaView
  activeCalendarViewId: string | null
}

type PersistedAgendaSettings = Partial<
  AgendaSettingsState & {
    autoImportInvitationIdentityKeys?: string[]
  }
>

type AgendaSettingsActions = {
  setQuickSettingsOpen: (open: boolean) => void
  setDefaultVideoProvider: (provider: AgendaVideoProvider) => void
  setVideoProviderApiKey: (provider: AgendaVideoProvider, key: string) => void
  setDefaultInvitationIdentityKey: (key: string | null) => void
  setAutoImportInvitationSources: (sources: AgendaAutoImportSource[]) => void
  setInvitationImportExclusions: (items: AgendaInvitationExclusion[]) => void
  setWeekStart: (weekStart: AgendaWeekStart) => void
  setDefaultQuickDurationMinutes: (minutes: number) => void
  setVisibleHoursStart: (minutes: number) => void
  setVisibleHoursEnd: (minutes: number) => void
  setTimeFormat: (format: AgendaTimeFormat) => void
  setDragSnapMinutes: (minutes: AgendaDurationStep) => void
  setButtonSnapMinutes: (minutes: AgendaDurationStep) => void
  toggleCalendarVisible: (id: string) => void
  setLastView: (view: AgendaView) => void
  setExternalCalendars: (calendars: AgendaExternalCalendar[]) => void
  addExternalCalendar: (input: Omit<AgendaExternalCalendar, "id">) => string
  updateExternalCalendar: (
    id: string,
    patch: Partial<Omit<AgendaExternalCalendar, "id">>,
  ) => void
  removeExternalCalendar: (id: string) => void
  setCalendarViews: (views: AgendaCalendarView[]) => void
  addCalendarView: (input: Omit<AgendaCalendarView, "id">) => string
  updateCalendarView: (
    id: string,
    patch: Partial<Omit<AgendaCalendarView, "id">>,
  ) => void
  removeCalendarView: (id: string) => void
  setDefaultCalendarViewId: (id: string | null) => void
  setActiveCalendarViewId: (id: string | null) => void
  setAccountEnabledCalendarIds: (
    accountId: string,
    calendarIds: string[] | null,
  ) => void
  toggleAccountCalendar: (
    accountId: string,
    calendarId: string,
    allCalendarIds: string[],
  ) => void
}

export const useAgendaSettingsStore = create<
  AgendaSettingsState & AgendaSettingsActions
>()(
  persist(
    (set, get) => ({
      ...DEFAULT_AGENDA_USER_SETTINGS,
      quickSettingsOpen: false,
      hiddenCalendarIds: [],
      lastView: "week",
      activeCalendarViewId: null,

      setQuickSettingsOpen: (quickSettingsOpen) => set({ quickSettingsOpen }),
      setDefaultVideoProvider: (defaultVideoProvider) => set({ defaultVideoProvider }),
      setVideoProviderApiKey: (provider, key) =>
        set((s) => ({
          videoProviderApiKeys: { ...s.videoProviderApiKeys, [provider]: key },
        })),
      setDefaultInvitationIdentityKey: (defaultInvitationIdentityKey) =>
        set({ defaultInvitationIdentityKey }),
      setAutoImportInvitationSources: (autoImportInvitationSources) =>
        set({
          autoImportInvitationSources: normalizeAutoImportInvitationSources(
            autoImportInvitationSources,
          ),
        }),
      setInvitationImportExclusions: (invitationImportExclusions) =>
        set({
          invitationImportExclusions: normalizeInvitationImportExclusions(
            invitationImportExclusions,
          ),
        }),
      setWeekStart: (weekStart) => set({ weekStart }),
      setDefaultQuickDurationMinutes: (defaultQuickDurationMinutes) =>
        set({ defaultQuickDurationMinutes }),
      setVisibleHoursStart: (visibleHoursStart) => set({ visibleHoursStart }),
      setVisibleHoursEnd: (visibleHoursEnd) => set({ visibleHoursEnd }),
      setTimeFormat: (timeFormat) => set({ timeFormat }),
      setDragSnapMinutes: (dragSnapMinutes) => set({ dragSnapMinutes }),
      setButtonSnapMinutes: (buttonSnapMinutes) => set({ buttonSnapMinutes }),
      toggleCalendarVisible: (id) => {
        const hidden = get().hiddenCalendarIds
        set({
          hiddenCalendarIds: hidden.includes(id)
            ? hidden.filter((h) => h !== id)
            : [...hidden, id],
        })
      },
      setLastView: (lastView) => set({ lastView }),
      setExternalCalendars: (externalCalendars) => set({ externalCalendars }),
      addExternalCalendar: (input) => {
        const id = newAgendaId(EXTERNAL_CALENDAR_ID_PREFIX)
        set((s) => ({
          externalCalendars: [...s.externalCalendars, { ...input, id }],
        }))
        return id
      },
      updateExternalCalendar: (id, patch) =>
        set((s) => ({
          externalCalendars: s.externalCalendars.map((calendar) =>
            calendar.id === id ? { ...calendar, ...patch } : calendar,
          ),
        })),
      removeExternalCalendar: (id) =>
        set((s) => ({
          externalCalendars: s.externalCalendars.filter((calendar) => calendar.id !== id),
          calendarViews: s.calendarViews.map((view) => ({
            ...view,
            calendar_ids: view.calendar_ids.filter((calendarId) => calendarId !== id),
          })),
          activeCalendarViewId:
            s.activeCalendarViewId === id ? null : s.activeCalendarViewId,
          hiddenCalendarIds: s.hiddenCalendarIds.filter((hiddenId) => hiddenId !== id),
          accountEnabledCalendarIds: Object.fromEntries(
            Object.entries(s.accountEnabledCalendarIds).map(([accountId, calendarIds]) => [
              accountId,
              calendarIds.filter((calendarId) => calendarId !== id),
            ]),
          ),
        })),
      setCalendarViews: (calendarViews) => set({ calendarViews }),
      addCalendarView: (input) => {
        const name = input.name.trim()
        if (isReservedAgendaViewName(name)) return ""
        const id = newAgendaId("view:")
        set((s) => ({
          calendarViews: [...s.calendarViews, { ...input, name, id }],
        }))
        return id
      },
      updateCalendarView: (id, patch) =>
        set((s) => {
          const nextName = patch.name?.trim()
          if (nextName && isReservedAgendaViewName(nextName)) return s
          return {
            calendarViews: s.calendarViews.map((view) =>
              view.id === id
                ? { ...view, ...patch, ...(nextName ? { name: nextName } : {}) }
                : view,
            ),
          }
        }),
      removeCalendarView: (id) =>
        set((s) => ({
          calendarViews: s.calendarViews.filter((view) => view.id !== id),
          defaultCalendarViewId:
            s.defaultCalendarViewId === id ? null : s.defaultCalendarViewId,
          activeCalendarViewId:
            s.activeCalendarViewId === id ? null : s.activeCalendarViewId,
        })),
      setDefaultCalendarViewId: (defaultCalendarViewId) =>
        set({ defaultCalendarViewId }),
      setActiveCalendarViewId: (activeCalendarViewId) => set({ activeCalendarViewId }),
      setAccountEnabledCalendarIds: (accountId, calendarIds) =>
        set((s) => {
          const next = { ...s.accountEnabledCalendarIds }
          if (calendarIds === null) {
            delete next[accountId]
          } else {
            next[accountId] = calendarIds
          }
          return { accountEnabledCalendarIds: next }
        }),
      toggleAccountCalendar: (accountId, calendarId, allCalendarIds) => {
        set((s) => {
          const enabled = s.accountEnabledCalendarIds[accountId] ?? allCalendarIds
          const next = enabled.includes(calendarId)
            ? enabled.filter((id) => id !== calendarId)
            : [...enabled, calendarId]
          const nextMap = { ...s.accountEnabledCalendarIds }
          if (
            next.length === allCalendarIds.length &&
            allCalendarIds.every((id) => next.includes(id))
          ) {
            delete nextMap[accountId]
          } else {
            nextMap[accountId] = next
          }
          return { accountEnabledCalendarIds: nextMap }
        })
      },
    }),
    {
      name: "agenda-settings-store",
      version: 3,
      migrate: (persisted, version) => {
        const state = persisted as PersistedAgendaSettings
        if (version < 1) {
          if (
            !state.autoImportInvitationSources &&
            Array.isArray(state.autoImportInvitationIdentityKeys)
          ) {
            state.autoImportInvitationSources = migrateAutoImportIdentityKeys(
              state.autoImportInvitationIdentityKeys,
            )
          }
          delete state.autoImportInvitationIdentityKeys
        }
        if (version < 2) {
          state.externalCalendars ??= []
          state.calendarViews ??= []
          state.defaultCalendarViewId ??= null
          state.accountEnabledCalendarIds ??= {}
          state.activeCalendarViewId ??= null
        }
        if (version < 3) {
          state.calendarViews = (state.calendarViews ?? []).map((view) =>
            isReservedAgendaViewName(view.name)
              ? { ...view, name: `${view.name} (vue)` }
              : view,
          )
        }
        return state as AgendaSettingsState
      },
      storage: debouncedPersistJSONStorage,
      partialize: (s) => ({
        defaultVideoProvider: s.defaultVideoProvider,
        videoProviderApiKeys: s.videoProviderApiKeys,
        defaultInvitationIdentityKey: s.defaultInvitationIdentityKey,
        autoImportInvitationSources: s.autoImportInvitationSources,
        invitationImportExclusions: s.invitationImportExclusions,
        weekStart: s.weekStart,
        defaultQuickDurationMinutes: s.defaultQuickDurationMinutes,
        visibleHoursStart: s.visibleHoursStart,
        visibleHoursEnd: s.visibleHoursEnd,
        timeFormat: s.timeFormat,
        dragSnapMinutes: s.dragSnapMinutes,
        buttonSnapMinutes: s.buttonSnapMinutes,
        hiddenCalendarIds: s.hiddenCalendarIds,
        lastView: s.lastView,
        externalCalendars: s.externalCalendars,
        calendarViews: s.calendarViews,
        defaultCalendarViewId: s.defaultCalendarViewId,
        accountEnabledCalendarIds: s.accountEnabledCalendarIds,
        activeCalendarViewId: s.activeCalendarViewId,
      }),
    },
  ),
)

interface AgendaUIState {
  sidebarCollapsed: boolean
  setSidebarCollapsed: (v: boolean) => void
}

export const useAgendaUIStore = create<AgendaUIState>((set) => ({
  sidebarCollapsed: false,
  setSidebarCollapsed: (sidebarCollapsed) => set({ sidebarCollapsed }),
}))

export function identityKey(email: string, accountId?: string): string {
  return `${accountId ?? ""}:${email}`
}
