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

import {
  DEFAULT_USER_SETTINGS,
  UserSettingsSchema,
  mergeSettings,
  parseUserSettings,
  type UserSettings,
} from '@/lib/user-settings'

describe('mergeSettings', () => {
  it('returns the patch when previous is empty', () => {
    const result = mergeSettings({}, { views: { sprintBacklog: { sort: 'code' } } })
    expect(result).toEqual({ views: { sprintBacklog: { sort: 'code' } } })
  })

  it('preserves existing keys when patch only sets new ones', () => {
    const prev: UserSettings = { views: { sprintBacklog: { sort: 'code' } } }
    const result = mergeSettings(prev, {
      views: { pbiList: { sort: 'date' } },
    })
    expect(result).toEqual({
      views: {
        sprintBacklog: { sort: 'code' },
        pbiList: { sort: 'date' },
      },
    })
  })

  it('merges nested objects without overwriting siblings', () => {
    const prev: UserSettings = {
      views: { sprintBacklog: { sort: 'code', sortDir: 'asc' } },
    }
    const result = mergeSettings(prev, {
      views: { sprintBacklog: { sort: 'priority' } },
    })
    expect(result).toEqual({
      views: { sprintBacklog: { sort: 'priority', sortDir: 'asc' } },
    })
  })

  it('replaces arrays instead of appending', () => {
    const prev: UserSettings = {
      views: { sprintBacklog: { collapsedPbis: ['a', 'b'] } },
    }
    const result = mergeSettings(prev, {
      views: { sprintBacklog: { collapsedPbis: ['c'] } },
    })
    expect(result.views?.sprintBacklog?.collapsedPbis).toEqual(['c'])
  })

  it('does not mutate the previous object', () => {
    const prev: UserSettings = { views: { sprintBacklog: { sort: 'code' } } }
    const snapshot = JSON.parse(JSON.stringify(prev))
    mergeSettings(prev, { views: { sprintBacklog: { sortDir: 'desc' } } })
    expect(prev).toEqual(snapshot)
  })

  it('skips undefined values in the patch', () => {
    const prev: UserSettings = { views: { sprintBacklog: { sort: 'code' } } }
    const result = mergeSettings(prev, { views: undefined })
    expect(result).toEqual(prev)
  })
})

describe('parseUserSettings', () => {
  it('returns defaults for null', () => {
    expect(parseUserSettings(null)).toEqual(DEFAULT_USER_SETTINGS)
  })

  it('returns defaults for undefined', () => {
    expect(parseUserSettings(undefined)).toEqual(DEFAULT_USER_SETTINGS)
  })

  it('returns defaults for invalid input', () => {
    expect(parseUserSettings({ views: { sprintBacklog: { filterStatus: 'BOGUS' } } }))
      .toEqual(DEFAULT_USER_SETTINGS)
  })

  it('passes valid settings through', () => {
    const valid = { views: { sprintBacklog: { sort: 'code' as const } } }
    expect(parseUserSettings(valid)).toEqual(valid)
  })
})

describe('UserSettingsSchema', () => {
  it('rejects unknown top-level keys', () => {
    const result = UserSettingsSchema.safeParse({ unknown: 1 })
    expect(result.success).toBe(false)
  })

  it('accepts an empty object', () => {
    expect(UserSettingsSchema.safeParse({}).success).toBe(true)
  })

  it('accepts the full shape', () => {
    const result = UserSettingsSchema.safeParse({
      views: {
        sprintBacklog: {
          filterPriority: 1,
          filterStatus: 'OPEN',
          sort: 'code',
          sortDir: 'asc',
          collapsedPbis: ['x'],
          filterPopoverOpen: true,
        },
        pbiList: { sort: 'priority', filterPriority: 'all', filterStatus: 'ready', sortDir: 'desc' },
        storyPanel: { sort: 'date' },
        jobsColumns: { 'queue:active': { kinds: ['TASK_IMPLEMENTATION'], statuses: [] } },
        jobs: { timeFilter: '24h' },
        ideasList: { filterStatuses: ['draft', 'planned'] },
      },
      devTools: { debugMode: true },
      layout: {
        splitPanePositions: { 'backlog-pid': [25, 35, 40] },
        activeSprints: { 'product-1': 'sprint-1' },
      },
    })
    expect(result.success).toBe(true)
  })

  it('accepts views.jobs.timeFilter and returns it via parseUserSettings', () => {
    const input = { views: { jobs: { timeFilter: '1h' as const } } }
    const result = parseUserSettings(input)
    expect(result).toEqual(input)
  })

  it('rejects an invalid views.jobs.timeFilter value', () => {
    const result = UserSettingsSchema.safeParse({ views: { jobs: { timeFilter: 'BOGUS' } } })
    expect(result.success).toBe(false)
  })

  it('accepts layout-only settings', () => {
    expect(UserSettingsSchema.safeParse({
      layout: { splitPanePositions: { x: [50, 50] }, activeSprints: { p: 's' } },
    }).success).toBe(true)
  })

  it('accepts null values in activeSprints (explicit "no active sprint")', () => {
    const result = UserSettingsSchema.safeParse({
      layout: { activeSprints: { 'product-1': null, 'product-2': 'sprint-2' } },
    })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.layout?.activeSprints).toEqual({
        'product-1': null,
        'product-2': 'sprint-2',
      })
    }
  })

  it('accepts pendingSprintDraft with per-PBI intent and overrides', () => {
    const result = UserSettingsSchema.safeParse({
      workflow: {
        pendingSprintDraft: {
          'product-1': {
            goal: 'Sprint goal',
            pbiIntent: { pbiA: 'all', pbiB: 'none' },
            storyOverrides: {
              pbiA: { add: [], remove: ['story-1'] },
              pbiB: { add: ['story-2'], remove: [] },
            },
          },
        },
      },
    })
    expect(result.success).toBe(true)
  })

  it('fills empty defaults for pbiIntent and storyOverrides in draft', () => {
    const result = UserSettingsSchema.safeParse({
      workflow: { pendingSprintDraft: { 'product-1': { goal: 'g' } } },
    })
    expect(result.success).toBe(true)
    if (result.success) {
      const draft = result.data.workflow?.pendingSprintDraft?.['product-1']
      expect(draft?.pbiIntent).toEqual({})
      expect(draft?.storyOverrides).toEqual({})
    }
  })

  it('rejects pendingSprintDraft with empty goal', () => {
    const result = UserSettingsSchema.safeParse({
      workflow: { pendingSprintDraft: { 'p': { goal: '' } } },
    })
    expect(result.success).toBe(false)
  })

  it('rejects an invalid ideasList.filterStatuses value', () => {
    const result = UserSettingsSchema.safeParse({ views: { ideasList: { filterStatuses: ['BOGUS'] } } })
    expect(result.success).toBe(false)
  })

  it('accepts an empty ideasList.filterStatuses array', () => {
    const result = UserSettingsSchema.safeParse({ views: { ideasList: { filterStatuses: [] } } })
    expect(result.success).toBe(true)
  })

  it('rejects unknown intent value', () => {
    const result = UserSettingsSchema.safeParse({
      workflow: {
        pendingSprintDraft: {
          p: { goal: 'x', pbiIntent: { a: 'partial' } },
        },
      },
    })
    expect(result.success).toBe(false)
  })
})
