import { describe, expect, it } from 'vitest'
import { ActivityEventSchema } from './activity-events'
import type { ActivityEvent } from './activity-events'

const TIMESTAMP = '2026-04-28T12:00:00.000Z'

function makeEvent<T extends ActivityEvent['type']>(
  type: T,
  overrides: Record<string, unknown> = {}
): Record<string, unknown> {
  const bases: Record<string, Record<string, unknown>> = {
    stage_change: { type, timestamp: TIMESTAMP, stageBefore: 'interested', stageAfter: 'proposal' },
    state_change: { type, timestamp: TIMESTAMP, stateBefore: null, stateAfter: 'discovery_replied' },
    action_taken: { type, timestamp: TIMESTAMP, actionKey: 'send_link' },
    approval_created: { type, timestamp: TIMESTAMP, commandId: 'cmd_abc' },
    approval_resolved: { type, timestamp: TIMESTAMP, commandId: 'cmd_abc', resolution: 'superseded' },
    approval_stale: { type, timestamp: TIMESTAMP, commandId: 'cmd_abc' },
    task_created: { type, timestamp: TIMESTAMP, taskId: 'task_001' },
    deal_created: { type, timestamp: TIMESTAMP }
  }
  return { ...bases[type], ...overrides }
}

function without(event: Record<string, unknown>, key: string): Record<string, unknown> {
  const copy = { ...event }
  delete copy[key]
  return copy
}

describe('ActivityEventSchema', () => {
  describe('positive cases — all 8 variants with minimal-valid payloads', () => {
    const variants: ActivityEvent['type'][] = [
      'stage_change',
      'state_change',
      'action_taken',
      'approval_created',
      'approval_resolved',
      'approval_stale',
      'task_created',
      'deal_created'
    ]

    it.each(variants)('accepts minimal valid %s event', (type) => {
      expect(ActivityEventSchema.safeParse(makeEvent(type)).success).toBe(true)
    })
  })

  describe('optional fields', () => {
    it('accepts stage_change without reason', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('stage_change')).success).toBe(true)
    })

    it('accepts stage_change with reason', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('stage_change', { reason: 'qualified' })).success).toBe(true)
    })

    it('accepts state_change without reason', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('state_change')).success).toBe(true)
    })

    it('accepts state_change with reason', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('state_change', { reason: 'manual override' })).success).toBe(true)
    })

    it('accepts action_taken without payload', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('action_taken')).success).toBe(true)
    })

    it('accepts action_taken with payload', () => {
      expect(
        ActivityEventSchema.safeParse(makeEvent('action_taken', { payload: { channel: 'email', count: 3 } })).success
      ).toBe(true)
    })

    it('accepts approval_created without dealStageBefore and dealStageAfter', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('approval_created')).success).toBe(true)
    })

    it('accepts approval_created with both stage fields', () => {
      expect(
        ActivityEventSchema.safeParse(
          makeEvent('approval_created', { dealStageBefore: 'interested', dealStageAfter: 'proposal' })
        ).success
      ).toBe(true)
    })

    it('accepts approval_resolved without originResourceType', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('approval_resolved')).success).toBe(true)
    })

    it('accepts approval_resolved with originResourceType', () => {
      expect(
        ActivityEventSchema.safeParse(makeEvent('approval_resolved', { originResourceType: 'crm_action' })).success
      ).toBe(true)
    })

    it('accepts approval_stale without dealStageBefore and dealStageAfter', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('approval_stale')).success).toBe(true)
    })

    it('accepts approval_stale with both stage fields', () => {
      expect(
        ActivityEventSchema.safeParse(
          makeEvent('approval_stale', { dealStageBefore: 'interested', dealStageAfter: 'proposal' })
        ).success
      ).toBe(true)
    })
  })

  describe('state_change nullable fields', () => {
    it('accepts stateBefore: null and stateAfter: null', () => {
      expect(
        ActivityEventSchema.safeParse(makeEvent('state_change', { stateBefore: null, stateAfter: null })).success
      ).toBe(true)
    })

    it('accepts stateBefore as string and stateAfter as string', () => {
      expect(
        ActivityEventSchema.safeParse(
          makeEvent('state_change', { stateBefore: 'discovery_link_sent', stateAfter: 'discovery_replied' })
        ).success
      ).toBe(true)
    })
  })

  describe('required-field enforcement', () => {
    it('rejects stage_change missing stageBefore', () => {
      expect(ActivityEventSchema.safeParse(without(makeEvent('stage_change'), 'stageBefore')).success).toBe(false)
    })

    it('rejects stage_change missing stageAfter', () => {
      expect(ActivityEventSchema.safeParse(without(makeEvent('stage_change'), 'stageAfter')).success).toBe(false)
    })

    it('rejects stage_change missing timestamp', () => {
      expect(ActivityEventSchema.safeParse(without(makeEvent('stage_change'), 'timestamp')).success).toBe(false)
    })

    it('rejects state_change missing stateBefore', () => {
      expect(ActivityEventSchema.safeParse(without(makeEvent('state_change'), 'stateBefore')).success).toBe(false)
    })

    it('rejects state_change missing stateAfter', () => {
      expect(ActivityEventSchema.safeParse(without(makeEvent('state_change'), 'stateAfter')).success).toBe(false)
    })

    it('rejects action_taken missing actionKey', () => {
      expect(ActivityEventSchema.safeParse(without(makeEvent('action_taken'), 'actionKey')).success).toBe(false)
    })

    it('rejects approval_created missing commandId', () => {
      expect(ActivityEventSchema.safeParse(without(makeEvent('approval_created'), 'commandId')).success).toBe(false)
    })

    it('rejects approval_resolved missing commandId', () => {
      expect(ActivityEventSchema.safeParse(without(makeEvent('approval_resolved'), 'commandId')).success).toBe(false)
    })

    it('rejects approval_resolved missing resolution', () => {
      expect(ActivityEventSchema.safeParse(without(makeEvent('approval_resolved'), 'resolution')).success).toBe(false)
    })

    it('rejects approval_stale missing commandId', () => {
      expect(ActivityEventSchema.safeParse(without(makeEvent('approval_stale'), 'commandId')).success).toBe(false)
    })

    it('rejects task_created missing taskId', () => {
      expect(ActivityEventSchema.safeParse(without(makeEvent('task_created'), 'taskId')).success).toBe(false)
    })
  })

  describe('approval_resolved enum constraint', () => {
    it('accepts resolution: superseded', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('approval_resolved', { resolution: 'superseded' })).success).toBe(
        true
      )
    })

    it('rejects resolution: approved', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('approval_resolved', { resolution: 'approved' })).success).toBe(
        false
      )
    })

    it('rejects resolution: rejected', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('approval_resolved', { resolution: 'rejected' })).success).toBe(
        false
      )
    })

    it('rejects resolution: pending', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('approval_resolved', { resolution: 'pending' })).success).toBe(
        false
      )
    })
  })

  describe('timestamp validation', () => {
    it('accepts a full ISO datetime string', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('deal_created', { timestamp: TIMESTAMP })).success).toBe(true)
    })

    it('rejects a non-date string', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('deal_created', { timestamp: 'not-a-date' })).success).toBe(false)
    })

    it('rejects a date-only string without time component', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('deal_created', { timestamp: '2026-04-28' })).success).toBe(false)
    })

    it('rejects an empty timestamp string', () => {
      expect(ActivityEventSchema.safeParse(makeEvent('deal_created', { timestamp: '' })).success).toBe(false)
    })
  })

  describe('discriminator strictness', () => {
    it('rejects an unknown type value', () => {
      expect(ActivityEventSchema.safeParse({ type: 'booking_nudge_sent', timestamp: TIMESTAMP }).success).toBe(false)
    })

    it('rejects a payload with no type field', () => {
      expect(ActivityEventSchema.safeParse({ timestamp: TIMESTAMP }).success).toBe(false)
    })

    it('rejects a payload where type is null', () => {
      expect(ActivityEventSchema.safeParse({ type: null, timestamp: TIMESTAMP }).success).toBe(false)
    })

    it('rejects a completely empty object', () => {
      expect(ActivityEventSchema.safeParse({}).success).toBe(false)
    })
  })

  describe('round-trip serialization', () => {
    it.each([
      'stage_change',
      'state_change',
      'action_taken',
      'approval_created',
      'approval_resolved',
      'approval_stale',
      'task_created',
      'deal_created'
    ] as const)('round-trips %s through JSON.stringify → JSON.parse', (type) => {
      const original = makeEvent(type)
      const parsed = JSON.parse(JSON.stringify(original))
      expect(ActivityEventSchema.safeParse(parsed).success).toBe(true)
    })
  })
})
