import {beforeEach, describe, expect, it, vi} from 'vitest'

import {PreviewSession} from './fixtures'
import {isPreviewEnabled} from './preview/utils'
import {getPerspective, getPerspectiveFromUrl} from './utils'
import {sanitizePerspective, supportsPerspectiveStack} from './utils'

describe('sanitizePerspective', () => {
  it('should split comma-separated string into array', () => {
    const result = sanitizePerspective('drafts,published')

    expect(result).toEqual(['drafts', 'published'])
  })

  it('should return "drafts" when validation fails', () => {
    const result = sanitizePerspective('raw')

    expect(result).toBe('drafts')
  })

  it('should filter out empty strings from perspective array', () => {
    // This happens when upstream sends [undefined, "releaseId", "drafts"]
    // which gets serialized as ",releaseId,drafts" and split back
    const result = sanitizePerspective(',releaseId,drafts')

    expect(result).toEqual(['releaseId', 'drafts'])
  })

  it('should filter out multiple empty strings', () => {
    const result = sanitizePerspective('drafts,,published,')

    expect(result).toEqual(['drafts', 'published'])
  })
})

describe('supportsPerspectiveStack', () => {
  it('should return false for version "1"', () => {
    expect(supportsPerspectiveStack('1')).toBe(false)
  })

  it('should return true for version "X"', () => {
    expect(supportsPerspectiveStack('X')).toBe(true)
  })

  it('should return false for versions before v2025-02-19', () => {
    expect(supportsPerspectiveStack('v2024-12-31')).toBe(false)
    expect(supportsPerspectiveStack('2024-12-31')).toBe(false)
    expect(supportsPerspectiveStack('v2025-02-18')).toBe(false)
    expect(supportsPerspectiveStack('2025-02-18')).toBe(false)
  })

  it('should return true for v2025-02-19', () => {
    expect(supportsPerspectiveStack('v2025-02-19')).toBe(true)
    expect(supportsPerspectiveStack('2025-02-19')).toBe(true)
  })

  it('should return true for versions after v2025-02-19', () => {
    expect(supportsPerspectiveStack('v2025-02-20')).toBe(true)
    expect(supportsPerspectiveStack('2025-02-20')).toBe(true)
    expect(supportsPerspectiveStack('v2025-03-01')).toBe(true)
    expect(supportsPerspectiveStack('2025-03-01')).toBe(true)
    expect(supportsPerspectiveStack('v2026-01-01')).toBe(true)
  })

  it('should return false for invalid date formats', () => {
    expect(supportsPerspectiveStack('invalid')).toBe(false)
    expect(supportsPerspectiveStack('2025-2-19')).toBe(false)
    expect(supportsPerspectiveStack('25-02-19')).toBe(false)
    expect(supportsPerspectiveStack('')).toBe(false)
  })
})

describe('getPerspective', () => {
  const mockSession = {
    get: vi.fn(),
    set: vi.fn(),
    unset: vi.fn(),
    commit: vi.fn(),
    has: vi.fn(),
    destroy: vi.fn(),
  }

  beforeEach(() => {
    vi.clearAllMocks()
  })

  it('should return perspective array when session has perspective data', () => {
    mockSession.has.mockReturnValue(true)
    mockSession.get.mockReturnValue('drafts,published')

    const result = getPerspective(mockSession)

    expect(result).toEqual(['drafts', 'published'])
    expect(mockSession.get).toHaveBeenCalledWith('perspective')
  })

  it('should filter out empty strings from perspective array', () => {
    // This happens when upstream sends [undefined, "releaseId", "drafts"]
    // which gets serialized as ",releaseId,drafts"
    mockSession.get.mockReturnValue(',releaseId,drafts')

    const result = getPerspective(mockSession)

    expect(result).toEqual(['releaseId', 'drafts'])
  })
})

describe('getPerspectiveFromUrl', () => {
  it('should return parsed perspective when URL param is present and valid', () => {
    expect(getPerspectiveFromUrl('https://example.com/?sanity-preview-perspective=drafts')).toBe(
      'drafts',
    )
  })

  it('should return undefined when param is absent', () => {
    expect(getPerspectiveFromUrl('https://example.com/')).toBeUndefined()
  })

  it('should return undefined when param is empty', () => {
    expect(
      getPerspectiveFromUrl('https://example.com/?sanity-preview-perspective='),
    ).toBeUndefined()
  })

  it('should handle comma-separated perspective stacks', () => {
    expect(
      getPerspectiveFromUrl('https://example.com/?sanity-preview-perspective=releaseId,drafts'),
    ).toEqual(['releaseId', 'drafts'])
  })

  it('should filter empty segments from perspective stacks', () => {
    expect(
      getPerspectiveFromUrl('https://example.com/?sanity-preview-perspective=,releaseId,drafts'),
    ).toEqual(['releaseId', 'drafts'])
  })

  it('should convert raw to drafts', () => {
    expect(getPerspectiveFromUrl('https://example.com/?sanity-preview-perspective=raw')).toBe(
      'drafts',
    )
  })

  it('should accept a URL object', () => {
    const url = new URL('https://example.com/?sanity-preview-perspective=drafts')
    expect(getPerspectiveFromUrl(url)).toBe('drafts')
  })
})

describe('isPreviewEnabled', () => {
  const projectId = 'test-project-id'

  it('should return true when session contains matching project ID', () => {
    const session = new PreviewSession()
    session.set('projectId', projectId)

    expect(isPreviewEnabled(projectId, session)).toBe(true)
  })

  it('should return false when session contains different project ID', () => {
    const session = new PreviewSession()
    session.set('projectId', 'different-project-id')

    expect(isPreviewEnabled(projectId, session)).toBe(false)
  })

  it('should return false when session contains no project ID', () => {
    const session = new PreviewSession()

    expect(isPreviewEnabled(projectId, session)).toBe(false)
  })

  it('should return false when no session is provided', () => {
    expect(isPreviewEnabled(projectId, undefined)).toBe(false)
  })

  it('should return false when session is null', () => {
    // @ts-expect-error Testing null session case
    expect(isPreviewEnabled(projectId, null)).toBe(false)
  })

  it('should handle empty project ID', () => {
    const session = new PreviewSession()
    session.set('projectId', 'some-project')

    expect(isPreviewEnabled('', session)).toBe(false)
  })

  it('should handle session with undefined project ID value', () => {
    const session = new PreviewSession()
    session.set('projectId', undefined)

    expect(isPreviewEnabled(projectId, session)).toBe(false)
  })

  it('should handle session with null project ID value', () => {
    const session = new PreviewSession()
    session.set('projectId', null)

    expect(isPreviewEnabled(projectId, session)).toBe(false)
  })

  it('should be case sensitive for project ID comparison', () => {
    const session = new PreviewSession()
    session.set('projectId', 'Test-Project-ID')

    expect(isPreviewEnabled('test-project-id', session)).toBe(false)
    expect(isPreviewEnabled('Test-Project-ID', session)).toBe(true)
  })

  it('should handle whitespace in project IDs', () => {
    const session = new PreviewSession()
    session.set('projectId', ' test-project-id ')

    expect(isPreviewEnabled('test-project-id', session)).toBe(false)
    expect(isPreviewEnabled(' test-project-id ', session)).toBe(true)
  })

  it('should work with Hydrogen session interface', () => {
    const hydrogenSession = {
      get: vi.fn().mockReturnValue(projectId),
      set: vi.fn(),
      unset: vi.fn(),
      has: vi.fn(),
      commit: vi.fn(),
      destroy: vi.fn(),
    }

    expect(isPreviewEnabled(projectId, hydrogenSession)).toBe(true)
    expect(hydrogenSession.get).toHaveBeenCalledWith('projectId')
  })

  it('should work with PreviewSession interface', () => {
    const previewSession = new PreviewSession()
    previewSession.set('projectId', projectId)

    expect(isPreviewEnabled(projectId, previewSession)).toBe(true)
  })
})
