import { describe, it, expect } from 'vitest'

// ---------------------------------------------------------------------------
// Kit definitions
// ---------------------------------------------------------------------------
describe('Kit definitions', () => {
  const KITS = {
    family: ['乳香', '茶树', '薰衣草', '柠檬', '椒样薄荷', '保卫', '牛至', '乐活', '顺畅呼吸', '舒缓'],
    home3988: ['乳香', '野橘', '柠檬', '薰衣草', '椒样薄荷', '冬青', '茶树', '生姜', '柠檬草', '西班牙牛至',
      '西洋蓍草石榴籽', '乐活', '保卫', '新瑞活力', '舒缓', '安定情绪', '安宁神气', '顺畅呼吸', '柑橘清新', '芳香调理', '元气焕能'],
    aroma: ['薰衣草', '舒缓', '安定情绪', '芳香调理', '野橘', '椒样薄荷', '保卫', '茶树'],
  }

  it('family kit has 10 oils', () => {
    expect(KITS.family).toHaveLength(10)
  })

  it('home3988 kit has 21 oils', () => {
    expect(KITS.home3988).toHaveLength(21)
  })

  it('aroma kit has 8 oils', () => {
    expect(KITS.aroma).toHaveLength(8)
    expect(KITS.aroma).toContain('薰衣草')
    expect(KITS.aroma).toContain('芳香调理')
    expect(KITS.aroma).toContain('茶树')
  })
})

// ---------------------------------------------------------------------------
// Recipe matching (exclude coconut oil)
// ---------------------------------------------------------------------------
describe('Recipe matching', () => {
  function matchRecipe(recipe, ownedSet) {
    const needed = recipe.ingredients.filter(i => i.oil !== '椰子油').map(i => i.oil)
    if (needed.length === 0) return false
    const coverage = needed.filter(o => ownedSet.has(o)).length
    return coverage >= 1
  }

  it('matches recipe when user has at least one oil', () => {
    const recipe = { ingredients: [{ oil: '乳香', drops: 3 }, { oil: '茶树', drops: 2 }, { oil: '椰子油', drops: 100 }] }
    expect(matchRecipe(recipe, new Set(['乳香']))).toBe(true)
  })

  it('does not match when user has none of the oils', () => {
    const recipe = { ingredients: [{ oil: '乳香', drops: 3 }, { oil: '茶树', drops: 2 }] }
    expect(matchRecipe(recipe, new Set(['薰衣草']))).toBe(false)
  })

  it('excludes coconut oil from matching', () => {
    const recipe = { ingredients: [{ oil: '椰子油', drops: 100 }] }
    expect(matchRecipe(recipe, new Set(['椰子油']))).toBe(false)
  })

  it('matches when user has all oils', () => {
    const recipe = { ingredients: [{ oil: '乳香', drops: 3 }, { oil: '茶树', drops: 2 }] }
    expect(matchRecipe(recipe, new Set(['乳香', '茶树']))).toBe(true)
  })
})

// ---------------------------------------------------------------------------
// Consumption analysis
// ---------------------------------------------------------------------------
describe('Consumption analysis', () => {
  function calcConsumption(ingredients, oilsMeta) {
    return ingredients
      .filter(i => i.oil && i.oil !== '椰子油' && i.drops > 0)
      .map(i => {
        const bottleDrops = oilsMeta[i.oil]?.dropCount || 0
        const sessions = bottleDrops > 0 ? Math.floor(bottleDrops / i.drops) : 0
        return { oil: i.oil, drops: i.drops, bottleDrops, sessions }
      })
  }

  function findLimit(data) {
    const valid = data.filter(c => c.sessions > 0)
    if (!valid.length) return { oil: '', sessions: 0, allSame: true }
    const min = Math.min(...valid.map(c => c.sessions))
    const allSame = valid.every(c => c.sessions === valid[0].sessions)
    const limiting = valid.find(c => c.sessions === min)
    return { oil: limiting.oil, sessions: min, allSame }
  }

  const meta = {
    '芳香调理': { dropCount: 280 },
    '薰衣草': { dropCount: 280 },
    '茶树': { dropCount: 280 },
  }

  it('calculates sessions correctly', () => {
    const data = calcConsumption([{ oil: '芳香调理', drops: 10 }], meta)
    expect(data[0].sessions).toBe(28)
  })

  it('finds limiting oil', () => {
    const data = calcConsumption([
      { oil: '芳香调理', drops: 10 },
      { oil: '薰衣草', drops: 20 },
    ], meta)
    const limit = findLimit(data)
    expect(limit.oil).toBe('薰衣草')
    expect(limit.sessions).toBe(14)
    expect(limit.allSame).toBe(false)
  })

  it('detects all same sessions', () => {
    const data = calcConsumption([
      { oil: '芳香调理', drops: 10 },
      { oil: '茶树', drops: 10 },
    ], meta)
    const limit = findLimit(data)
    expect(limit.allSame).toBe(true)
    expect(limit.sessions).toBe(28)
  })

  it('excludes coconut oil', () => {
    const data = calcConsumption([
      { oil: '芳香调理', drops: 10 },
      { oil: '椰子油', drops: 200 },
    ], meta)
    expect(data).toHaveLength(1)
    expect(data[0].oil).toBe('芳香调理')
  })
})

// ---------------------------------------------------------------------------
// Project pricing persistence
// ---------------------------------------------------------------------------
describe('Project pricing JSON', () => {
  it('serializes extra fields to JSON', () => {
    const extra = { selling_price: 299, packaging_cost: 5, labor_cost: 30, other_cost: 10, quantity: 1 }
    const json = JSON.stringify(extra)
    const parsed = JSON.parse(json)
    expect(parsed.selling_price).toBe(299)
    expect(parsed.quantity).toBe(1)
  })

  it('merges extra fields back into project', () => {
    const project = { id: 1, name: 'test', ingredients: [], pricing: '{"selling_price":299,"quantity":1}' }
    const extra = JSON.parse(project.pricing)
    const merged = { ...project, ...extra }
    expect(merged.selling_price).toBe(299)
    expect(merged.quantity).toBe(1)
  })
})
