import { describe, it, expect, beforeEach } from 'vitest'
import { useBacklogStore } from '@/stores/backlog-store'
import type { BacklogPbi, BacklogStory, BacklogTask } from '@/stores/backlog-store'

const PBI: BacklogPbi = {
  id: 'pbi-1',
  code: 'PBI-1',
  title: 'Realtime PBI',
  priority: 2,
  description: 'desc',
  created_at: new Date('2024-01-01T00:00:00Z'),
  status: 'ready',
}

const STORY: BacklogStory = {
  id: 'story-1',
  code: 'ST-1',
  title: 'Realtime story',
  description: null,
  acceptance_criteria: null,
  priority: 2,
  status: 'OPEN',
  pbi_id: 'pbi-1',
  created_at: new Date('2024-01-01T00:00:00Z'),
}

const TASK: BacklogTask = {
  id: 'task-1',
  title: 'Realtime task',
  description: null,
  priority: 2,
  status: 'TO_DO',
  sort_order: 1,
  story_id: 'story-1',
  created_at: new Date('2024-01-01T00:00:00Z'),
}

beforeEach(() => {
  useBacklogStore.setState({ pbis: [], storiesByPbi: {}, tasksByStory: {} })
})

// ---------------------------------------------------------------------------
// PBI
// ---------------------------------------------------------------------------

describe('PBI payload contract', () => {
  it('INSERT: entity appears in pbis with correct title and status', () => {
    useBacklogStore.getState().applyChange('pbi', 'I', { ...PBI })
    const state = useBacklogStore.getState()
    expect(state.pbis).toHaveLength(1)
    expect(state.pbis[0].id).toBe('pbi-1')
    expect(state.pbis[0].title).toBe('Realtime PBI')
    expect(state.pbis[0].status).toBe('ready')
  })

  it('INSERT is idempotent: duplicate SSE-event does not add a second entry', () => {
    useBacklogStore.getState().applyChange('pbi', 'I', { ...PBI })
    useBacklogStore.getState().applyChange('pbi', 'I', { ...PBI })
    expect(useBacklogStore.getState().pbis).toHaveLength(1)
  })

  it('UPDATE: changed_fields partial merges into existing entity', () => {
    useBacklogStore.setState({ pbis: [{ ...PBI }], storiesByPbi: {}, tasksByStory: {} })
    useBacklogStore.getState().applyChange('pbi', 'U', { id: 'pbi-1', title: 'Updated PBI', status: 'in_sprint' as const })
    const pbi = useBacklogStore.getState().pbis[0]
    expect(pbi.title).toBe('Updated PBI')
    expect(pbi.status).toBe('in_sprint')
    expect(pbi.priority).toBe(2) // unchanged field retained
  })

  it('DELETE: entity is removed from pbis', () => {
    useBacklogStore.setState({ pbis: [{ ...PBI }], storiesByPbi: {}, tasksByStory: {} })
    useBacklogStore.getState().applyChange('pbi', 'D', { id: 'pbi-1' })
    expect(useBacklogStore.getState().pbis).toHaveLength(0)
  })
})

// ---------------------------------------------------------------------------
// Story
// ---------------------------------------------------------------------------

describe('Story payload contract', () => {
  it('INSERT: entity appears in storiesByPbi[pbi_id] with correct title and status', () => {
    useBacklogStore.setState({ pbis: [], storiesByPbi: { 'pbi-1': [] }, tasksByStory: {} })
    useBacklogStore.getState().applyChange('story', 'I', { ...STORY })
    const bucket = useBacklogStore.getState().storiesByPbi['pbi-1']
    expect(bucket).toHaveLength(1)
    expect(bucket[0].id).toBe('story-1')
    expect(bucket[0].title).toBe('Realtime story')
    expect(bucket[0].status).toBe('OPEN')
  })

  it('INSERT: creates bucket when pbi_id was not yet in storiesByPbi', () => {
    useBacklogStore.getState().applyChange('story', 'I', { ...STORY })
    expect(useBacklogStore.getState().storiesByPbi['pbi-1']).toHaveLength(1)
  })

  it('INSERT is idempotent: duplicate SSE-event does not add a second entry', () => {
    useBacklogStore.getState().applyChange('story', 'I', { ...STORY })
    useBacklogStore.getState().applyChange('story', 'I', { ...STORY })
    expect(useBacklogStore.getState().storiesByPbi['pbi-1']).toHaveLength(1)
  })

  it('UPDATE: changed_fields partial merges into existing story', () => {
    useBacklogStore.setState({ pbis: [], storiesByPbi: { 'pbi-1': [{ ...STORY }] }, tasksByStory: {} })
    useBacklogStore.getState().applyChange('story', 'U', { id: 'story-1', title: 'Updated story', status: 'IN_SPRINT' })
    const story = useBacklogStore.getState().storiesByPbi['pbi-1'][0]
    expect(story.title).toBe('Updated story')
    expect(story.status).toBe('IN_SPRINT')
    expect(story.priority).toBe(2) // unchanged field retained
  })

  it('DELETE: entity is removed from its pbi bucket', () => {
    useBacklogStore.setState({ pbis: [], storiesByPbi: { 'pbi-1': [{ ...STORY }] }, tasksByStory: {} })
    useBacklogStore.getState().applyChange('story', 'D', { id: 'story-1' })
    expect(useBacklogStore.getState().storiesByPbi['pbi-1']).toHaveLength(0)
  })
})

// ---------------------------------------------------------------------------
// Task
// ---------------------------------------------------------------------------

describe('Task payload contract', () => {
  it('INSERT: entity appears in tasksByStory[story_id] with correct title and status', () => {
    useBacklogStore.setState({ pbis: [], storiesByPbi: {}, tasksByStory: { 'story-1': [] } })
    useBacklogStore.getState().applyChange('task', 'I', { ...TASK })
    const bucket = useBacklogStore.getState().tasksByStory['story-1']
    expect(bucket).toHaveLength(1)
    expect(bucket[0].id).toBe('task-1')
    expect(bucket[0].title).toBe('Realtime task')
    expect(bucket[0].status).toBe('TO_DO')
  })

  it('INSERT: creates bucket when story_id was not yet in tasksByStory', () => {
    useBacklogStore.getState().applyChange('task', 'I', { ...TASK })
    expect(useBacklogStore.getState().tasksByStory['story-1']).toHaveLength(1)
  })

  it('INSERT is idempotent: duplicate SSE-event does not add a second entry', () => {
    useBacklogStore.getState().applyChange('task', 'I', { ...TASK })
    useBacklogStore.getState().applyChange('task', 'I', { ...TASK })
    expect(useBacklogStore.getState().tasksByStory['story-1']).toHaveLength(1)
  })

  it('UPDATE: changed_fields partial merges into existing task', () => {
    useBacklogStore.setState({ pbis: [], storiesByPbi: {}, tasksByStory: { 'story-1': [{ ...TASK }] } })
    useBacklogStore.getState().applyChange('task', 'U', { id: 'task-1', title: 'Updated task', status: 'IN_PROGRESS' })
    const task = useBacklogStore.getState().tasksByStory['story-1'][0]
    expect(task.title).toBe('Updated task')
    expect(task.status).toBe('IN_PROGRESS')
    expect(task.sort_order).toBe(1) // unchanged field retained
  })

  it('DELETE: entity is removed from its story bucket', () => {
    useBacklogStore.setState({ pbis: [], storiesByPbi: {}, tasksByStory: { 'story-1': [{ ...TASK }] } })
    useBacklogStore.getState().applyChange('task', 'D', { id: 'task-1' })
    expect(useBacklogStore.getState().tasksByStory['story-1']).toHaveLength(0)
  })
})
