import { describe, expect, it } from 'vitest'
import { type StatefulPipelineDefinition } from '../../organization-model/domains/sales'
import { ActivityEventSchema } from './activity-events'
import { DealStageSchema, TransitionItemRequestSchema } from './api-schemas'
import { deriveActions } from './derive-actions'
import type { DealStage } from './types'

const DEAL_STAGES = [
  'interested',
  'proposal',
  'closing',
  'closed_won',
  'closed_lost',
  'nurturing'
] as const satisfies readonly DealStage[]

const CRM_PIPELINE_FIXTURE: StatefulPipelineDefinition = {
  pipelineKey: 'crm',
  label: 'CRM',
  entityKey: 'crm.deal',
  stages: DEAL_STAGES.map((stageKey, index) => ({
    stageKey,
    label: stageKey,
    color: 'gray',
    states:
      index === 0
        ? [
            { stateKey: 'discovery_replied', label: 'Discovery Replied' },
            { stateKey: 'discovery_link_sent', label: 'Discovery Link Sent' },
            { stateKey: 'discovery_nudging', label: 'Discovery Nudging' },
            { stateKey: 'discovery_booking_cancelled', label: 'Discovery Booking Cancelled' }
          ]
        : []
  }))
}

const CRM_ACTION_FIXTURE = [
  {
    key: 'move_to_proposal',
    label: 'Move to Proposal',
    workflowId: 'move_to_proposal-workflow',
    isAvailableFor: (deal: Parameters<typeof deriveActions>[0]) => deal.stage_key === 'interested'
  },
  {
    key: 'move_to_closing',
    label: 'Move to Closing',
    workflowId: 'move_to_closing-workflow',
    isAvailableFor: (deal: Parameters<typeof deriveActions>[0]) => deal.stage_key === 'proposal'
  },
  {
    key: 'move_to_closed_won',
    label: 'Close Won',
    workflowId: 'move_to_closed_won-workflow',
    isAvailableFor: (deal: Parameters<typeof deriveActions>[0]) => deal.stage_key === 'closing'
  },
  {
    key: 'move_to_closed_lost',
    label: 'Close Lost',
    workflowId: 'move_to_closed_lost-workflow',
    isAvailableFor: (deal: Parameters<typeof deriveActions>[0]) =>
      deal.stage_key === 'interested' || deal.stage_key === 'proposal' || deal.stage_key === 'closing'
  },
  {
    key: 'move_to_nurturing',
    label: 'Move to Nurturing',
    workflowId: 'move_to_nurturing-workflow',
    isAvailableFor: (deal: Parameters<typeof deriveActions>[0]) =>
      deal.stage_key === 'interested' || deal.stage_key === 'proposal' || deal.stage_key === 'closing'
  },
  {
    key: 'send_link',
    label: 'Send Booking Link',
    workflowId: 'crm-send-booking-link-workflow',
    isAvailableFor: (deal: Parameters<typeof deriveActions>[0]) =>
      deal.stage_key === 'interested' && deal.state_key === 'discovery_replied'
  },
  {
    key: 'send_nudge',
    label: 'Send Nudge',
    workflowId: 'crm-send-nudge-workflow',
    isAvailableFor: (deal: Parameters<typeof deriveActions>[0]) =>
      deal.stage_key === 'interested' &&
      (deal.state_key === 'discovery_link_sent' || deal.state_key === 'discovery_nudging')
  },
  {
    key: 'mark_no_show',
    label: 'Mark No-Show',
    workflowId: 'mark_no_show-workflow',
    isAvailableFor: (deal: Parameters<typeof deriveActions>[0]) =>
      deal.stage_key === 'interested' && deal.state_key === 'discovery_nudging'
  },
  {
    key: 'rebook',
    label: 'Rebook',
    workflowId: 'crm-rebook-workflow',
    isAvailableFor: (deal: Parameters<typeof deriveActions>[0]) =>
      deal.stage_key === 'interested' && deal.state_key === 'discovery_booking_cancelled'
  }
]

function deal(stageKey: string | null, stateKey: string | null = null): Parameters<typeof deriveActions>[0] {
  return { stage_key: stageKey, state_key: stateKey } as Parameters<typeof deriveActions>[0]
}

describe('CRM deal action derivation', () => {
  it('derives the exact base actions for interested deals from caller-supplied actions', () => {
    expect(deriveActions(deal('interested'), CRM_ACTION_FIXTURE)).toEqual([
      { key: 'move_to_proposal', label: 'Move to Proposal' },
      { key: 'move_to_closed_lost', label: 'Close Lost' },
      { key: 'move_to_nurturing', label: 'Move to Nurturing' }
    ])
  })

  it('derives no actions unless the caller supplies an action catalog', () => {
    expect(deriveActions(deal('interested'))).toEqual([])
  })

  it.each([
    ['discovery_replied', { key: 'send_link', label: 'Send Booking Link' }],
    ['discovery_link_sent', { key: 'send_nudge', label: 'Send Nudge' }],
    ['discovery_booking_cancelled', { key: 'rebook', label: 'Rebook' }]
  ] as const)('adds the expected workflow action for interested/%s', (stateKey, expectedAction) => {
    expect(deriveActions(deal('interested', stateKey), CRM_ACTION_FIXTURE)).toContainEqual(expectedAction)
  })

  it('adds nudge and no-show actions for interested/discovery_nudging', () => {
    expect(deriveActions(deal('interested', 'discovery_nudging'), CRM_ACTION_FIXTURE)).toEqual([
      { key: 'move_to_proposal', label: 'Move to Proposal' },
      { key: 'move_to_closed_lost', label: 'Close Lost' },
      { key: 'move_to_nurturing', label: 'Move to Nurturing' },
      { key: 'send_nudge', label: 'Send Nudge' },
      { key: 'mark_no_show', label: 'Mark No-Show' }
    ])
  })
})

describe('ActivityEventSchema', () => {
  const timestamp = '2026-04-27T12:34:56.000Z'

  it.each([
    [{ type: 'stage_change', timestamp, stageBefore: 'interested', stageAfter: 'proposal', reason: 'qualified' }],
    [{ type: 'state_change', timestamp, stateBefore: null, stateAfter: 'discovery_replied' }],
    [{ type: 'action_taken', timestamp, actionKey: 'send_link', payload: { channel: 'email' } }],
    [{ type: 'approval_created', timestamp, commandId: 'cmd_123', dealStageBefore: 'proposal' }],
    [{ type: 'approval_resolved', timestamp, commandId: 'cmd_123', resolution: 'superseded' }],
    [{ type: 'approval_stale', timestamp, commandId: 'cmd_123', dealStageAfter: 'closing' }],
    [{ type: 'task_created', timestamp, taskId: 'task_123' }],
    [{ type: 'deal_created', timestamp }]
  ])('accepts expected %s events', (event) => {
    expect(ActivityEventSchema.safeParse(event).success).toBe(true)
  })

  it.each([
    [{ type: 'deal_created', timestamp: 'not-a-date' }],
    [{ type: 'unknown_event', timestamp }],
    [{ type: 'stage_change', timestamp, stageBefore: 'interested' }],
    [{ type: 'approval_resolved', timestamp, commandId: 'cmd_123', resolution: 'approved' }],
    [{ type: 'booking_nudge_sent', timestamp, followupDay: '2' }],
    [{ type: 'action_taken', timestamp }]
  ])('rejects malformed event payload %o', (event) => {
    expect(ActivityEventSchema.safeParse(event).success).toBe(false)
  })
})

describe('CRM stage and transition vocabulary contracts', () => {
  it('keeps DealStage and DealStageSchema aligned with a caller-owned CRM fixture', () => {
    const crmStageKeys = CRM_PIPELINE_FIXTURE.stages.map((stage) => stage.stageKey)

    expect(crmStageKeys).toEqual([...DEAL_STAGES])
    for (const stageKey of crmStageKeys) {
      expect(DealStageSchema.safeParse(stageKey).success).toBe(true)
    }
  })

  it('accepts every fixture CRM deal stage in transition requests', () => {
    for (const stageKey of DEAL_STAGES) {
      expect(
        TransitionItemRequestSchema.safeParse({
          pipelineKey: CRM_PIPELINE_FIXTURE.pipelineKey,
          stageKey,
          stateKey: null,
          expectedUpdatedAt: '2026-04-27T12:34:56.000Z'
        }).success
      ).toBe(true)
    }
  })

  it('accepts caller-supplied lead-gen stage/state pairs in transition requests', () => {
    const leadGenPipelines: Record<string, StatefulPipelineDefinition[]> = {
      'acq.list-member': [
        {
          pipelineKey: 'lead-gen',
          label: 'Lead Gen',
          entityKey: 'leadgen.contact',
          stages: [
            {
              stageKey: 'outreach',
              label: 'Outreach',
              color: 'blue',
              states: [
                { stateKey: 'personalized', label: 'Personalized' },
                { stateKey: 'contacted', label: 'Contacted' }
              ]
            }
          ]
        }
      ]
    }

    for (const pipelineDefinitions of Object.values(leadGenPipelines)) {
      for (const pipeline of pipelineDefinitions) {
        for (const stage of pipeline.stages) {
          for (const state of stage.states) {
            expect(
              TransitionItemRequestSchema.safeParse({
                pipelineKey: pipeline.pipelineKey,
                stageKey: stage.stageKey,
                stateKey: state.stateKey
              }).success
            ).toBe(true)
          }
        }
      }
    }
  })
})
