import { describe, expect, it } from 'vitest'
import {
  OfferingsDomainSchema,
  ProductSchema,
  PricingModelSchema,
  DEFAULT_ORGANIZATION_MODEL_OFFERINGS
} from '../../domains/offerings'
import { resolveOrganizationModel } from '../../resolve'

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

describe('ProductSchema — positive parse', () => {
  it('accepts a fully-populated product', () => {
    const result = ProductSchema.safeParse({
      id: 'product-starter-plan',
      order: 10,
      name: 'Starter Plan',
      description: 'Full-service automation for small teams.',
      pricingModel: 'subscription',
      price: 299,
      currency: 'USD',
      targetSegmentIds: ['seg-smb'],
      deliveryFeatureId: 'sales.crm'
    })
    expect(result.success).toBe(true)
  })

  it('accepts a minimal product — only id and order required', () => {
    const result = ProductSchema.safeParse({ id: 'product-minimal', order: 10 })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.id).toBe('product-minimal')
      expect(result.data.name).toBe('')
      expect(result.data.description).toBe('')
      expect(result.data.pricingModel).toBe('custom')
      expect(result.data.price).toBe(0)
      expect(result.data.currency).toBe('USD')
      expect(result.data.targetSegmentIds).toEqual([])
      expect(result.data.deliveryFeatureId).toBeUndefined()
    }
  })

  it('trims whitespace from id and name', () => {
    const result = ProductSchema.safeParse({
      id: '  product-trim  ',
      order: 10,
      name: '  Trimmed Name  '
    })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.id).toBe('product-trim')
      expect(result.data.name).toBe('Trimmed Name')
    }
  })

  it('accepts price of 0 (free tier)', () => {
    const result = ProductSchema.safeParse({
      id: 'product-free',
      order: 10,
      pricingModel: 'subscription',
      price: 0
    })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.price).toBe(0)
    }
  })

  it('accepts all four pricing model values', () => {
    const models = ['one-time', 'subscription', 'usage-based', 'custom'] as const
    for (const pricingModel of models) {
      const result = ProductSchema.safeParse({ id: `product-${pricingModel}`, order: 10, pricingModel })
      expect(result.success).toBe(true)
    }
  })
})

// ---------------------------------------------------------------------------
// Group 2: ProductSchema — default values
// ---------------------------------------------------------------------------

describe('ProductSchema — default values', () => {
  it('pricingModel defaults to "custom"', () => {
    const result = ProductSchema.safeParse({ id: 'product-defaults', order: 10 })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.pricingModel).toBe('custom')
    }
  })

  it('price defaults to 0', () => {
    const result = ProductSchema.safeParse({ id: 'product-defaults', order: 10 })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.price).toBe(0)
    }
  })

  it('currency defaults to "USD"', () => {
    const result = ProductSchema.safeParse({ id: 'product-defaults', order: 10 })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.currency).toBe('USD')
    }
  })

  it('targetSegmentIds defaults to empty array', () => {
    const result = ProductSchema.safeParse({ id: 'product-defaults', order: 10 })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.targetSegmentIds).toEqual([])
    }
  })

  it('deliveryFeatureId defaults to undefined (field is optional)', () => {
    const result = ProductSchema.safeParse({ id: 'product-defaults', order: 10 })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data.deliveryFeatureId).toBeUndefined()
    }
  })
})

// ---------------------------------------------------------------------------
// Group 3: ProductSchema — negative parse (wrong types / constraints)
// ---------------------------------------------------------------------------

describe('ProductSchema — negative parse', () => {
  it('rejects missing id', () => {
    const result = ProductSchema.safeParse({ order: 10, name: 'No ID Product' })
    expect(result.success).toBe(false)
  })

  it('rejects empty string id', () => {
    const result = ProductSchema.safeParse({ id: '', order: 10 })
    expect(result.success).toBe(false)
  })

  it('rejects whitespace-only id (trims to empty string)', () => {
    const result = ProductSchema.safeParse({ id: '   ', order: 10 })
    expect(result.success).toBe(false)
  })

  it('rejects an unknown pricingModel value', () => {
    const result = ProductSchema.safeParse({ id: 'product-bad-model', order: 10, pricingModel: 'freemium' })
    expect(result.success).toBe(false)
  })

  it('rejects price below 0', () => {
    const result = ProductSchema.safeParse({ id: 'product-negative-price', order: 10, price: -1 })
    expect(result.success).toBe(false)
  })

  it('rejects price as a string', () => {
    const result = ProductSchema.safeParse({ id: 'product-string-price', order: 10, price: '299' })
    expect(result.success).toBe(false)
  })

  it('rejects targetSegmentIds as a non-array value', () => {
    const result = ProductSchema.safeParse({ id: 'product-bad-segments', order: 10, targetSegmentIds: 'seg-a' })
    expect(result.success).toBe(false)
  })
})

// ---------------------------------------------------------------------------
// Group 4: PricingModelSchema — enum coverage
// ---------------------------------------------------------------------------

describe('PricingModelSchema — enum', () => {
  it('has exactly four values', () => {
    const result = PricingModelSchema.safeParse('one-time')
    expect(result.success).toBe(true)
    // Verify the full enum options list matches expected set
    expect(PricingModelSchema.options).toEqual(['one-time', 'subscription', 'usage-based', 'custom'])
  })

  it('rejects an empty string', () => {
    const result = PricingModelSchema.safeParse('')
    expect(result.success).toBe(false)
  })

  it('rejects a numeric value', () => {
    const result = PricingModelSchema.safeParse(42)
    expect(result.success).toBe(false)
  })
})

// ---------------------------------------------------------------------------
// Group 5: OfferingsDomainSchema — structural
// ---------------------------------------------------------------------------

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

  it('defaults products to empty record when omitted', () => {
    const result = OfferingsDomainSchema.safeParse({})
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data).toEqual({})
    }
  })

  it('accepts multiple valid products', () => {
    const result = OfferingsDomainSchema.safeParse({
      'product-a': { id: 'product-a', order: 10, name: 'Product A', pricingModel: 'subscription' },
      'product-b': { id: 'product-b', order: 20, name: 'Product B', pricingModel: 'one-time', price: 999 }
    })
    expect(result.success).toBe(true)
    if (result.success) {
      expect(Object.keys(result.data)).toHaveLength(2)
    }
  })

  it('rejects a record where entry id does not match map key', () => {
    const result = OfferingsDomainSchema.safeParse({
      'product-a': { id: 'product-b', order: 10, name: 'Mismatched' }
    })
    expect(result.success).toBe(false)
  })

  it('DEFAULT_ORGANIZATION_MODEL_OFFERINGS constant matches schema parse of empty object', () => {
    const result = OfferingsDomainSchema.safeParse({})
    expect(result.success).toBe(true)
    if (result.success) {
      expect(result.data).toEqual(DEFAULT_ORGANIZATION_MODEL_OFFERINGS)
    }
  })
})

// ---------------------------------------------------------------------------
// Group 6: Cross-ref validation via resolveOrganizationModel
// ---------------------------------------------------------------------------

describe('resolveOrganizationModel — offerings cross-ref (targetSegmentIds)', () => {
  it('passes when targetSegmentIds references a declared customer segment', () => {
    expect(() =>
      resolveOrganizationModel({
        customers: {
          'seg-agencies': { id: 'seg-agencies', order: 10, name: 'SMB Agencies' }
        },
        offerings: {
          'product-starter': {
            id: 'product-starter',
            order: 10,
            name: 'Starter Plan',
            pricingModel: 'subscription',
            targetSegmentIds: ['seg-agencies']
          }
        }
      })
    ).not.toThrow()
  })

  it('throws when targetSegmentIds references a non-existent segment', () => {
    expect(() =>
      resolveOrganizationModel({
        customers: {},
        offerings: {
          'product-bad-ref': {
            id: 'product-bad-ref',
            order: 10,
            targetSegmentIds: ['nonexistent-segment']
          }
        }
      })
    ).toThrow()
  })

  it('throws with a message referencing the unknown segment id', () => {
    let errorMessage = ''
    try {
      resolveOrganizationModel({
        customers: {},
        offerings: {
          'product-path-check': {
            id: 'product-path-check',
            order: 10,
            targetSegmentIds: ['ghost-segment']
          }
        }
      })
    } catch (e) {
      errorMessage = String(e)
    }
    expect(errorMessage).toContain('ghost-segment')
  })

  it('passes when targetSegmentIds is empty (no refs required)', () => {
    expect(() =>
      resolveOrganizationModel({
        offerings: {
          'product-no-targets': { id: 'product-no-targets', order: 10, targetSegmentIds: [] }
        }
      })
    ).not.toThrow()
  })

  it('passes when multiple products each reference a valid segment', () => {
    expect(() =>
      resolveOrganizationModel({
        customers: {
          'seg-a': { id: 'seg-a', order: 10, name: 'Segment A' },
          'seg-b': { id: 'seg-b', order: 20, name: 'Segment B' }
        },
        offerings: {
          'product-1': { id: 'product-1', order: 10, targetSegmentIds: ['seg-a'] },
          'product-2': { id: 'product-2', order: 20, targetSegmentIds: ['seg-a', 'seg-b'] }
        }
      })
    ).not.toThrow()
  })
})

// ---------------------------------------------------------------------------
// Group 7: Cross-ref validation — deliveryFeatureId
// ---------------------------------------------------------------------------

describe('resolveOrganizationModel — offerings cross-ref (deliveryFeatureId)', () => {
  it('passes when deliveryFeatureId references a declared system', () => {
    expect(() =>
      resolveOrganizationModel({
        systems: {
          'sales.crm': {
            id: 'sales.crm',
            order: 10,
            label: 'CRM',
            lifecycle: 'active'
          }
        },
        offerings: {
          'product-with-delivery': {
            id: 'product-with-delivery',
            order: 10,
            deliveryFeatureId: 'sales.crm'
          }
        }
      })
    ).not.toThrow()
  })

  it('throws when deliveryFeatureId references a non-existent system', () => {
    expect(() =>
      resolveOrganizationModel({
        offerings: {
          'product-bad-delivery': {
            id: 'product-bad-delivery',
            order: 10,
            deliveryFeatureId: 'nonexistent-feature'
          }
        }
      })
    ).toThrow()
  })

  it('throws with a message referencing the unknown system id', () => {
    let errorMessage = ''
    try {
      resolveOrganizationModel({
        offerings: {
          'product-delivery-path': {
            id: 'product-delivery-path',
            order: 10,
            deliveryFeatureId: 'ghost-feature'
          }
        }
      })
    } catch (e) {
      errorMessage = String(e)
    }
    expect(errorMessage).toContain('ghost-feature')
  })

  it('passes when deliveryFeatureId is absent (field is optional)', () => {
    expect(() =>
      resolveOrganizationModel({
        offerings: {
          'product-no-delivery': { id: 'product-no-delivery', order: 10 }
        }
      })
    ).not.toThrow()
  })
})

// ---------------------------------------------------------------------------
// Group 8: Integration — resolveOrganizationModel general
// ---------------------------------------------------------------------------

describe('resolveOrganizationModel — offerings domain integration', () => {
  it('merges partial offerings override and preserves empty products default', () => {
    const model = resolveOrganizationModel({ offerings: {} })
    expect(Object.keys(model.offerings)).toHaveLength(0)
  })

  it('omitting offerings key entirely resolves to default empty products', () => {
    const model = resolveOrganizationModel({})
    expect(model.offerings).toBeDefined()
    expect(Object.keys(model.offerings)).toHaveLength(0)
  })

  it('does not bleed offerings changes into other top-level domains', () => {
    const model = resolveOrganizationModel({
      customers: {
        'seg-isolation': { id: 'seg-isolation', order: 10 }
      },
      offerings: {
        'product-isolation': { id: 'product-isolation', order: 10, targetSegmentIds: ['seg-isolation'] }
      }
    })
    expect(model.identity).toBeDefined()
    expect(model.customers).toBeDefined()
    expect(model.systems).toBeDefined()
    // Phase 4 (D8): model.navigation removed from top-level OM.
    expect(model.navigation).toBeDefined()
    expect(model.knowledge).toBeDefined()
  })
})
