import { describe, expect, it, vi } from 'vitest'
import { buildLeadProposalDraft } from '../src/queries/lead-proposal'
import { getLeadStageChangeForProposalStatus } from '../src/queries/proposals'

describe('lead-to-proposal draft prefills', () => {
  it('builds the proposal subject and intro section from the lead company and latest note preview', () => {
    const draft = buildLeadProposalDraft({
      company: 'Acme Corp',
      leadName: 'Ada Lovelace',
      notePreview: 'Latest discovery note',
      estimatedValue: '2500.00',
    })

    expect(draft.subject).toBe('Proposal for Acme Corp')
    expect(draft.notes).toBe('Latest discovery note')
    expect(draft.totalAmount).toBe('2500.00')
    expect(draft.content.sections[0]).toMatchObject({
      type: 'text',
      html: '<p>Latest discovery note</p>',
    })
    expect(draft.content.sections[1]).toMatchObject({
      type: 'line_items',
      items: [],
    })
  })

  it('falls back to the lead name and default intro when no note exists', () => {
    const draft = buildLeadProposalDraft({
      company: null,
      leadName: 'Ada Lovelace',
      notePreview: null,
      estimatedValue: null,
    })

    expect(draft.subject).toBe('Proposal for Ada Lovelace')
    expect(draft.notes).toBeNull()
    expect(draft.totalAmount).toBe('0')
    expect((draft.content.sections[0] as { html: string }).html).toContain('Proposal prepared for Ada Lovelace.')
  })
})

describe('proposal lifecycle to lead stage mapping', () => {
  it('advances early lead stages to PROPOSAL when a proposal is sent', () => {
    expect(getLeadStageChangeForProposalStatus('NEW', 'sent')).toEqual({
      nextStage: 'PROPOSAL',
      activityContent: 'Proposal sent',
    })
    expect(getLeadStageChangeForProposalStatus('QUALIFIED', 'sent')).toEqual({
      nextStage: 'PROPOSAL',
      activityContent: 'Proposal sent',
    })
    expect(getLeadStageChangeForProposalStatus('PROPOSAL', 'sent')).toBeNull()
  })

  it('marks accepted and rejected proposals as terminal lead stages', () => {
    expect(getLeadStageChangeForProposalStatus('CONTACTED', 'accepted')).toEqual({
      nextStage: 'WON',
      activityContent: 'Proposal accepted — lead marked Won',
    })
    expect(getLeadStageChangeForProposalStatus('PROPOSAL', 'rejected')).toEqual({
      nextStage: 'LOST',
      activityContent: 'Proposal rejected — lead marked Lost',
    })
  })
})

describe('createProposalFromLead', () => {
  it('writes and returns a generated public token for the new draft proposal', async () => {
    const lead = {
      id: 'lead-1',
      tenantId: 'tenant-1',
      customerId: 'customer-1',
      company: 'Acme Corp',
      name: 'Ada Lovelace',
      estimatedValue: '2500.00',
    }
    const proposalRow = {
      id: 'proposal-1',
      tenantId: 'tenant-1',
      customerId: 'customer-1',
      leadId: 'lead-1',
      contractId: null,
      title: 'Proposal for Acme Corp',
      name: 'Proposal for Acme Corp',
      status: 'draft',
      content: { sections: [], settings: { show_line_tax: true, show_subtotal: true, discount_pct: 0, currency: 'USD' } },
      lineItems: [],
      subtotal: '2500.00',
      discount: null,
      total: '2500.00',
      totalAmount: '2500.00',
      notes: 'Latest discovery note',
      validUntil: null,
      expiresAt: null,
      sentAt: null,
      publicToken: 'public-token-1',
      viewCount: 0,
      firstViewedAt: null,
      lastViewedAt: null,
      locale: 'en',
      createdBy: 'user-1',
      createdAt: new Date('2026-06-30T00:00:00.000Z'),
      updatedAt: new Date('2026-06-30T00:00:00.000Z'),
    }

    const insertLeadActivityValues = vi.fn().mockResolvedValue(undefined)
    const insertAuditValues = vi.fn().mockResolvedValue(undefined)
    const insertProposalReturning = vi.fn().mockResolvedValue([proposalRow])
    const insertProposalValues = vi.fn(() => ({ returning: insertProposalReturning }))
    const insertMock = vi
      .fn()
      .mockImplementationOnce(() => ({ values: insertProposalValues }))
      .mockImplementationOnce(() => ({ values: insertLeadActivityValues }))
      .mockImplementationOnce(() => ({ values: insertAuditValues }))

    const tx = {
      select: vi
        .fn()
        .mockImplementationOnce(() => ({
          from: vi.fn(() => ({
            where: vi.fn(() => ({
              limit: vi.fn().mockResolvedValue([lead]),
            })),
          })),
        }))
        .mockImplementationOnce(() => ({
          from: vi.fn(() => ({
            where: vi.fn(() => ({
              orderBy: vi.fn(() => ({
                limit: vi.fn().mockResolvedValue([{ content: 'Latest discovery note' }]),
              })),
            })),
          })),
        })),
      insert: insertMock,
    }

    const db = {
      transaction: vi.fn(async (fn: (trx: typeof tx) => Promise<unknown>) => fn(tx)),
    }

    const { createProposalFromLead } = await import('../src/queries/lead-proposal')
    const result = await createProposalFromLead(db as never, 'tenant-1', 'user-1', 'lead-1')

    expect(insertProposalValues).toHaveBeenCalledWith(expect.objectContaining({
      tenantId: 'tenant-1',
      leadId: 'lead-1',
      customerId: 'customer-1',
      title: 'Proposal for Acme Corp',
      name: 'Proposal for Acme Corp',
      publicToken: expect.any(String),
      createdBy: 'user-1',
    }))
    expect(result.publicToken).toBe('public-token-1')
    expect(insertLeadActivityValues).toHaveBeenCalledWith(expect.objectContaining({
      tenantId: 'tenant-1',
      leadId: 'lead-1',
      metadata: { proposalId: 'proposal-1' },
    }))
    expect(insertAuditValues).toHaveBeenCalledWith(expect.objectContaining({
      tenantId: 'tenant-1',
      actorId: 'user-1',
      entityId: 'proposal-1',
      action: 'proposal.created_from_lead',
    }))
  })
})
