import { describe, expect, it } from 'vitest'
import type { QueueTaskStatus } from '../../../commands/queue/types/task'
import type { TaskStatus } from '../../../business/projects/types'
import {
  DEFAULT_ORGANIZATION_MODEL_STATUSES,
  StatusEntrySchema,
  StatusesDomainSchema,
  StatusSemanticClassSchema
} from '../../domains/statuses'

// ---------------------------------------------------------------------------
// Group 1: StatusEntrySchema — positive parse
// ---------------------------------------------------------------------------

describe('StatusEntrySchema — positive parse', () => {
  it('accepts a fully-specified entry (id, label, semanticClass, category)', () => {
    const result = StatusEntrySchema.safeParse({
      id: 'delivery.task.planned',
      order: 10,
      label: 'Planned',
      semanticClass: 'delivery.task',
      category: 'delivery'
    })
    expect(result.success).toBe(true)
  })

  it('accepts a minimal entry without category (category is optional)', () => {
    const result = StatusEntrySchema.safeParse({
      id: 'queue.pending',
      order: 10,
      label: 'Pending',
      semanticClass: 'queue'
    })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.category).toBeUndefined()
    }
  })

  it('trims whitespace from id and label', () => {
    const result = StatusEntrySchema.safeParse({
      id: '  trimmed-id  ',
      order: 10,
      label: '  Trimmed Label  ',
      semanticClass: 'execution'
    })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.id).toBe('trimmed-id')
      expect(result.data.label).toBe('Trimmed Label')
    }
  })
})

// ---------------------------------------------------------------------------
// Group 2: StatusEntrySchema — negative parse
// ---------------------------------------------------------------------------

describe('StatusEntrySchema — negative parse', () => {
  it('rejects a missing id field', () => {
    const result = StatusEntrySchema.safeParse({
      label: 'No ID',
      semanticClass: 'queue'
    })
    expect(result.success).toBe(false)
  })

  it('rejects a missing label field', () => {
    const result = StatusEntrySchema.safeParse({
      id: 'queue.pending',
      semanticClass: 'queue'
    })
    expect(result.success).toBe(false)
  })

  it('rejects a missing semanticClass field', () => {
    const result = StatusEntrySchema.safeParse({
      id: 'queue.pending',
      label: 'Pending'
    })
    expect(result.success).toBe(false)
  })

  it('rejects an unknown semanticClass value', () => {
    const result = StatusEntrySchema.safeParse({
      id: 'some.entry',
      label: 'Some Entry',
      semanticClass: 'not-a-real-class'
    })
    expect(result.success).toBe(false)
  })

  it('rejects a non-string id (number)', () => {
    const result = StatusEntrySchema.safeParse({
      id: 42,
      label: 'Label',
      semanticClass: 'queue'
    })
    expect(result.success).toBe(false)
  })

  it('rejects an empty string label (min length 1 after trim)', () => {
    const result = StatusEntrySchema.safeParse({
      id: 'some.entry',
      label: '   ',
      semanticClass: 'queue'
    })
    expect(result.success).toBe(false)
  })
})

// ---------------------------------------------------------------------------
// Group 3: StatusesDomainSchema — structural tests
// ---------------------------------------------------------------------------

describe('StatusesDomainSchema — structural tests', () => {
  it('accepts an empty record', () => {
    const result = StatusesDomainSchema.safeParse({})
    expect(result.success).toBe(true)
  })

  it('applies an empty-record default when the domain is omitted', () => {
    const result = StatusesDomainSchema.safeParse(undefined)
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data).toEqual({})
    }
  })
})

// ---------------------------------------------------------------------------
// Group 4: Semantic class enum — all 8 values declared
// ---------------------------------------------------------------------------

describe('StatusSemanticClassSchema — enum coverage', () => {
  const expectedClasses = [
    'delivery.task',
    'delivery.project',
    'delivery.milestone',
    'queue',
    'execution',
    'schedule',
    'schedule.run',
    'request'
  ] as const

  it('enum exposes exactly 8 semantic classes', () => {
    expect(StatusSemanticClassSchema.options).toHaveLength(8)
  })

  it.each(expectedClasses)('"%s" is a valid semanticClass value', (cls) => {
    const result = StatusSemanticClassSchema.safeParse(cls)
    expect(result.success).toBe(true)
  })

  it('every entry in the seed uses one of the 8 known semanticClass values', () => {
    const valid = new Set<string>(StatusSemanticClassSchema.options)
    for (const entry of Object.values(DEFAULT_ORGANIZATION_MODEL_STATUSES)) {
      expect(
        valid.has(entry.semanticClass),
        `Unexpected semanticClass "${entry.semanticClass}" on entry "${entry.id}"`
      ).toBe(true)
    }
  })

  it('all 8 semanticClass values have at least one seed entry', () => {
    const presentClasses = new Set(Object.values(DEFAULT_ORGANIZATION_MODEL_STATUSES).map((e) => e.semanticClass))
    for (const cls of StatusSemanticClassSchema.options) {
      expect(presentClasses.has(cls), `No seed entry found for semanticClass "${cls}"`).toBe(true)
    }
  })
})

// ---------------------------------------------------------------------------
// Group 5: Seed completeness — per-group counts
// ---------------------------------------------------------------------------

describe('DEFAULT_ORGANIZATION_MODEL_STATUSES seed — per-group counts', () => {
  function countBySemClass(cls: string) {
    return Object.values(DEFAULT_ORGANIZATION_MODEL_STATUSES).filter((e) => e.semanticClass === cls).length
  }

  it('has 6 delivery.project entries', () => {
    expect(countBySemClass('delivery.project')).toBe(6)
  })

  it('has 5 delivery.milestone entries', () => {
    expect(countBySemClass('delivery.milestone')).toBe(5)
  })

  it('has 5 execution entries', () => {
    expect(countBySemClass('execution')).toBe(5)
  })

  it('has 4 schedule entries', () => {
    expect(countBySemClass('schedule')).toBe(4)
  })

  it('has 4 schedule.run entries', () => {
    expect(countBySemClass('schedule.run')).toBe(4)
  })

  it('has 4 request entries', () => {
    expect(countBySemClass('request')).toBe(4)
  })
})

// ---------------------------------------------------------------------------
// Group 6: QueueTaskStatus values — all 5 present in seed with class "queue"
// ---------------------------------------------------------------------------

describe('QueueTaskStatus coverage in seed', () => {
  const queueValues: QueueTaskStatus[] = ['pending', 'processing', 'completed', 'failed', 'expired']
  const queueIds = Object.values(DEFAULT_ORGANIZATION_MODEL_STATUSES)
    .filter((e) => e.semanticClass === 'queue')
    .map((e) => e.id)

  it.each(queueValues)('queue.%s is present as a seed entry id', (status) => {
    expect(queueIds).toContain(`queue.${status}`)
  })
})

// ---------------------------------------------------------------------------
// Group 7: Delivery TaskStatus values — all 9 present in seed with class "delivery.task"
// ---------------------------------------------------------------------------

describe('TaskStatus (delivery) coverage in seed', () => {
  const taskValues: TaskStatus[] = [
    'planned',
    'in_progress',
    'blocked',
    'submitted',
    'approved',
    'revision_requested',
    'rejected',
    'cancelled',
    'completed'
  ]
  const taskIds = Object.values(DEFAULT_ORGANIZATION_MODEL_STATUSES)
    .filter((e) => e.semanticClass === 'delivery.task')
    .map((e) => e.id)

  it.each(taskValues)('delivery.task.%s is present as a seed entry id', (status) => {
    expect(taskIds).toContain(`delivery.task.${status}`)
  })
})
