import { describe, expect, test } from 'vitest'
import {
  combineDateTime,
  extractTime,
  formatIsoDate,
  formatIsoDateTime,
  localTzOffset,
  nowIsoLocal,
  parseFormDate,
} from './dates'

describe('parseFormDate', () => {
  test('возвращает null для не-строки и пустой строки', () => {
    expect(parseFormDate(null)).toBeNull()
    expect(parseFormDate(undefined)).toBeNull()
    expect(parseFormDate(123)).toBeNull()
    expect(parseFormDate('')).toBeNull()
  })

  test('pure-date YYYY-MM-DD парсится как локальная полуночь (без TZ-сдвига)', () => {
    const d = parseFormDate('2026-05-08')!
    expect(d.getFullYear()).toBe(2026)
    expect(d.getMonth()).toBe(4) // май = 4 (zero-indexed)
    expect(d.getDate()).toBe(8)
    expect(d.getHours()).toBe(0)
    expect(d.getMinutes()).toBe(0)
  })

  test('ISO datetime с offset парсится корректно', () => {
    const d = parseFormDate('2026-05-08T14:30:00+03:00')!
    expect(d).toBeInstanceOf(Date)
    expect(Number.isNaN(d.getTime())).toBe(false)
  })

  test('far-future (year >= 9999) возвращает null', () => {
    expect(parseFormDate('9999-12-31T23:59:59Z')).toBeNull()
    expect(parseFormDate('10000-01-01')).toBeNull()
  })

  test('невалидная строка → null', () => {
    expect(parseFormDate('not-a-date')).toBeNull()
  })
})

describe('formatIsoDate', () => {
  test('форматирует Date как YYYY-MM-DD в local TZ', () => {
    const d = new Date(2026, 4, 8) // 8 мая 2026 local
    expect(formatIsoDate(d)).toBe('2026-05-08')
  })

  test('паддит month/day двумя цифрами', () => {
    const d = new Date(2026, 0, 5)
    expect(formatIsoDate(d)).toBe('2026-01-05')
  })
})

describe('localTzOffset', () => {
  test('возвращает offset в формате ±HH:mm', () => {
    const off = localTzOffset(new Date())
    expect(off).toMatch(/^[+-]\d{2}:\d{2}$/)
  })
})

describe('formatIsoDateTime', () => {
  test('собирает ISO с временем по умолчанию 00:00', () => {
    const d = new Date(2026, 4, 8) // 8 мая 2026 local
    const iso = formatIsoDateTime(d)
    expect(iso).toMatch(/^2026-05-08T00:00:00[+-]\d{2}:\d{2}$/)
  })

  test('применяет переданное время', () => {
    const d = new Date(2026, 4, 8)
    const iso = formatIsoDateTime(d, '14:30')
    expect(iso).toMatch(/^2026-05-08T14:30:00[+-]\d{2}:\d{2}$/)
  })

  test('round-trip: parse → format не меняет дату/время', () => {
    const orig = formatIsoDateTime(new Date(2026, 4, 8), '14:30')
    const parsed = parseFormDate(orig)!
    expect(parsed.getFullYear()).toBe(2026)
    expect(parsed.getMonth()).toBe(4)
    expect(parsed.getDate()).toBe(8)
    expect(parsed.getHours()).toBe(14)
    expect(parsed.getMinutes()).toBe(30)
  })
})

describe('extractTime', () => {
  test('возвращает HH:mm из ISO datetime', () => {
    // 14:30 в local TZ — формируем явно через formatIsoDateTime
    const iso = formatIsoDateTime(new Date(2026, 4, 8), '14:30')
    expect(extractTime(iso, '00:00')).toBe('14:30')
  })

  test('возвращает fallback для пустой строки', () => {
    expect(extractTime(undefined, '23:59')).toBe('23:59')
    expect(extractTime('', '00:00')).toBe('00:00')
  })

  test('возвращает fallback для невалидной строки', () => {
    expect(extractTime('garbage', '12:34')).toBe('12:34')
  })

  test('паддит часы и минуты двумя цифрами', () => {
    const iso = formatIsoDateTime(new Date(2026, 4, 8), '05:07')
    expect(extractTime(iso, '00:00')).toBe('05:07')
  })
})

describe('combineDateTime', () => {
  test('null Date → пустая строка (для clear button)', () => {
    expect(combineDateTime(null, '14:30', 'previous')).toBe('')
  })

  test('собирает ISO datetime из Date + HH:mm', () => {
    const d = new Date(2026, 4, 8)
    const iso = combineDateTime(d, '14:30', '')
    expect(iso).toMatch(/^2026-05-08T14:30:00[+-]\d{2}:\d{2}$/)
  })

  test('защитный fallback на 00:00 при невалидном time', () => {
    const d = new Date(2026, 4, 8)
    const iso = combineDateTime(d, 'invalid', '')
    expect(iso).toMatch(/^2026-05-08T00:00:00[+-]\d{2}:\d{2}$/)
  })
})

describe('nowIsoLocal', () => {
  test('возвращает ISO datetime в local TZ', () => {
    const iso = nowIsoLocal()
    expect(iso).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:00[+-]\d{2}:\d{2}$/)
  })

  test('parsable обратно в Date', () => {
    const iso = nowIsoLocal()
    const d = parseFormDate(iso)
    expect(d).not.toBeNull()
  })
})
