import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { APP_STORAGE_KEYS, LEGACY_APP_STORAGE_KEYS } from '../constants/appStorage'
import { allSelection, makeEntry } from '../test-utils/noteListTestUtils'
import {
  clearListSortFromLocalStorage,
  countInboxByPeriod,
  extractSortableProperties,
  filterEntries,
  filterInboxEntries,
  formatSearchSubtitle,
  formatSubtitle,
  getSortComparator,
  getSortOptionLabel,
  loadSortPreferences,
  parseSortConfig,
  relativeDate,
  saveSortPreferences,
  serializeSortConfig,
} from './noteListHelpers'

const localStorageMock = (() => {
  let store: Record<string, string> = {}
  return {
    getItem: (key: string) => store[key] ?? null,
    setItem: (key: string, value: string) => { store[key] = value },
    removeItem: (key: string) => { delete store[key] },
    clear: () => { store = {} },
  }
})()

Object.defineProperty(globalThis, 'localStorage', {
  value: localStorageMock,
  writable: true,
})

describe('noteListHelpers extra coverage', () => {
  beforeEach(() => {
    vi.useFakeTimers()
    vi.setSystemTime(new Date('2026-04-21T12:00:00Z'))
    localStorage.clear()
  })

  afterEach(() => {
    vi.restoreAllMocks()
    vi.useRealTimers()
  })

  it('formats relative dates across future, recent, and older timestamps', () => {
    const nowSeconds = Math.floor(Date.now() / 1000)

    expect(relativeDate(nowSeconds + 86400)).toBe('Apr 22')
    expect(relativeDate(nowSeconds - 30)).toBe('just now')
    expect(relativeDate(nowSeconds - 5 * 60)).toBe('5m ago')
    expect(relativeDate(nowSeconds - 2 * 3600)).toBe('2h ago')
    expect(relativeDate(nowSeconds - 3 * 86400)).toBe('3d ago')
    expect(relativeDate(nowSeconds - 10 * 86400)).toBe('Apr 11')
  })

  it('builds note subtitles for empty, linked, and edited notes', () => {
    const modifiedEntry = makeEntry({
      title: 'Project',
      modifiedAt: Math.floor(Date.now() / 1000) - 3600,
      createdAt: Math.floor(Date.now() / 1000) - 86400 * 2,
      wordCount: 1200,
      outgoingLinks: ['alpha', 'beta'],
    })
    const emptyEntry = makeEntry({
      title: 'Empty',
      modifiedAt: null,
      createdAt: null,
      wordCount: 0,
      outgoingLinks: [],
    })

    expect(formatSubtitle(modifiedEntry)).toBe('April 21, 2026 · 1,200 words · 2 links')
    expect(formatSubtitle(emptyEntry)).toBe('Empty')
    expect(formatSearchSubtitle(modifiedEntry)).toBe('April 21, 2026 · Created April 19, 2026 · 1,200 words · 2 links')
  })

  it('keeps note subtitle counts stable under non-English default number formatting', () => {
    const originalToLocaleString = Number.prototype.toLocaleString
    vi.spyOn(Number.prototype, 'toLocaleString').mockImplementation(function (
      this: number,
      locales?: Intl.LocalesArgument,
      options?: Intl.NumberFormatOptions,
    ) {
      return originalToLocaleString.call(this, locales ?? 'de-DE', options)
    })

    const entry = makeEntry({
      title: 'Project',
      modifiedAt: Math.floor(Date.now() / 1000) - 3600,
      createdAt: Math.floor(Date.now() / 1000) - 86400 * 2,
      wordCount: 1200,
      outgoingLinks: ['alpha', 'beta'],
    })

    expect(formatSubtitle(entry)).toBe('April 21, 2026 · 1,200 words · 2 links')
    expect(formatSearchSubtitle(entry)).toBe('April 21, 2026 · Created April 19, 2026 · 1,200 words · 2 links')
    expect(formatSubtitle(entry, 'iso')).toBe('2026-04-21 · 1,200 words · 2 links')
    expect(formatSearchSubtitle(entry, 'european')).toBe('21/4/2026 · Created 19/4/2026 · 1,200 words · 2 links')
  })

  it('extracts sortable properties and labels custom property sort keys', () => {
    const entries = [
      makeEntry({ properties: { Priority: 'High', Owner: 'Luca' } }),
      makeEntry({ properties: { Estimate: 3, Priority: 'Low' } }),
    ]

    expect(extractSortableProperties(entries)).toEqual(['Estimate', 'Owner', 'Priority'])
    expect(getSortOptionLabel('property:Priority')).toBe('Priority')
    expect(getSortOptionLabel('title')).toBe('Title')
  })

  it('sorts entries by built-in and custom property comparators', () => {
    const entries = [
      makeEntry({
        title: 'Gamma',
        createdAt: 10,
        modifiedAt: 30,
        status: 'Done',
        properties: { Score: 5, Start: '2026-04-18', Enabled: true },
      }),
      makeEntry({
        title: 'Alpha',
        createdAt: 20,
        modifiedAt: 20,
        status: 'Active',
        properties: { Score: 2, Start: '2026-04-15', Enabled: false },
      }),
      makeEntry({
        title: 'Beta',
        createdAt: 15,
        modifiedAt: 25,
        status: null,
        properties: { Score: 8, Start: 'not-a-date', Enabled: true },
      }),
    ]

    expect([...entries].sort(getSortComparator('title', 'asc')).map((entry) => entry.title)).toEqual(['Alpha', 'Beta', 'Gamma'])
    expect([...entries].sort(getSortComparator('created', 'desc')).map((entry) => entry.title)).toEqual(['Alpha', 'Beta', 'Gamma'])
    expect([...entries].sort(getSortComparator('status', 'asc')).map((entry) => entry.title)).toEqual(['Alpha', 'Gamma', 'Beta'])
    expect([...entries].sort(getSortComparator('property:Score', 'asc')).map((entry) => entry.title)).toEqual(['Alpha', 'Gamma', 'Beta'])
    expect([...entries].sort(getSortComparator('property:Start', 'asc')).map((entry) => entry.title)).toEqual(['Alpha', 'Gamma', 'Beta'])
    expect([...entries].sort(getSortComparator('property:Enabled', 'asc')).map((entry) => entry.title)).toEqual(['Alpha', 'Gamma', 'Beta'])

    const entriesWithMissingTitle = [
      makeEntry({ title: 'Beta' }),
      makeEntry({ title: null as unknown as string }),
    ]
    expect([...entriesWithMissingTitle].sort(getSortComparator('title', 'asc')).map((entry) => entry.title)).toEqual([null, 'Beta'])
  })

  it('serializes, parses, loads, and saves sort preferences with migration support', () => {
    const serialized = serializeSortConfig({ option: 'property:Priority', direction: 'desc' })
    expect(serialized).toBe('property:Priority:desc')
    expect(parseSortConfig(serialized)).toEqual({ option: 'property:Priority', direction: 'desc' })
    expect(parseSortConfig('date:desc')).toEqual({ option: 'property:date', direction: 'desc' })
    expect(parseSortConfig('broken')).toBeNull()
    expect(parseSortConfig('title:sideways')).toBeNull()
    expect(parseSortConfig('property::asc')).toBeNull()

    localStorage.setItem(APP_STORAGE_KEYS.sortPreferences, JSON.stringify({
      '__list__': 'title',
      'type:Project': { option: 'created', direction: 'asc' },
    }))

    expect(loadSortPreferences()).toEqual({
      '__list__': { option: 'title', direction: 'asc' },
      'type:Project': { option: 'created', direction: 'asc' },
    })

    saveSortPreferences({
      '__list__': { option: 'modified', direction: 'desc' },
    })

    expect(localStorage.getItem(APP_STORAGE_KEYS.sortPreferences)).toBe(JSON.stringify({
      '__list__': { option: 'modified', direction: 'desc' },
    }))
    expect(localStorage.getItem(LEGACY_APP_STORAGE_KEYS.sortPreferences)).toBeNull()

    clearListSortFromLocalStorage()
    expect(localStorage.getItem(APP_STORAGE_KEYS.sortPreferences)).toBeNull()
    expect(localStorage.getItem(LEGACY_APP_STORAGE_KEYS.sortPreferences)).toBeNull()
  })

  it('filters view, folder, favorites, and pulse selections', () => {
    const entries = [
      makeEntry({
        path: '/vault/notes/alpha.md',
        title: 'Alpha',
        fileKind: 'markdown',
        favorite: true,
      }),
      makeEntry({
        path: '/vault/projects/beta.md',
        title: 'Beta',
        fileKind: 'markdown',
      }),
      makeEntry({
        path: '/vault/attachments/diagram.png',
        title: 'Diagram',
        fileKind: 'binary',
      }),
    ]
    const views = [{
      filename: 'work.view',
      definition: {
        name: 'Work',
        icon: null,
        color: null,
        sort: null,
        filters: {
          all: [{ field: 'title', op: 'contains', value: 'Alpha' }],
        },
      },
    }]

    expect(filterEntries(entries, { kind: 'view', filename: 'work.view' }, { views }).map((entry) => entry.title)).toEqual(['Alpha'])
    expect(filterEntries(entries, { kind: 'folder', path: 'projects' }).map((entry) => entry.title)).toEqual(['Beta'])
    expect(filterEntries(entries, { kind: 'filter', filter: 'favorites' }).map((entry) => entry.title)).toEqual(['Alpha'])
    expect(filterEntries(entries, { kind: 'filter', filter: 'pulse' })).toEqual([])
    expect(filterEntries(entries, allSelection).map((entry) => entry.title)).toEqual(['Alpha', 'Beta'])
  })

  it('filters inbox entries by period and counts them', () => {
    const nowSeconds = Math.floor(Date.now() / 1000)
    const entries = [
      makeEntry({
        title: 'This Week',
        organized: false,
        archived: false,
        isA: 'Note',
        createdAt: nowSeconds - 2 * 86400,
      }),
      makeEntry({
        title: 'This Month',
        organized: false,
        archived: false,
        isA: 'Note',
        createdAt: nowSeconds - 20 * 86400,
      }),
      makeEntry({
        title: 'This Quarter',
        organized: false,
        archived: false,
        isA: 'Note',
        createdAt: nowSeconds - 80 * 86400,
      }),
      makeEntry({
        title: 'Organized',
        organized: true,
        archived: false,
        isA: 'Note',
        createdAt: nowSeconds - 2 * 86400,
      }),
      makeEntry({
        title: 'Type document',
        organized: false,
        archived: false,
        isA: 'Type',
        createdAt: nowSeconds - 2 * 86400,
      }),
    ]

    expect(filterInboxEntries(entries, 'week').map((entry) => entry.title)).toEqual(['This Week'])
    expect(filterInboxEntries(entries, 'month').map((entry) => entry.title)).toEqual(['This Week', 'This Month'])
    expect(filterInboxEntries(entries, 'quarter').map((entry) => entry.title)).toEqual(['This Week', 'This Month', 'This Quarter'])
    expect(countInboxByPeriod(entries)).toEqual({
      week: 1,
      month: 2,
      quarter: 3,
      all: 3,
    })
  })
})
