describe('Oil Data Integrity', () => {
  it('all oils have valid prices', () => {
    cy.request('/api/oils').then(res => {
      res.body.forEach(oil => {
        expect(oil.name).to.be.a('string').and.not.be.empty
        expect(oil.bottle_price).to.be.a('number').and.be.gte(0)
        expect(oil.drop_count).to.be.a('number').and.be.gt(0)
      })
    })
  })

  it('common oils exist in the database', () => {
    const expected = ['薰衣草', '茶树', '乳香', '柠檬', '椒样薄荷', '椰子油']
    cy.request('/api/oils').then(res => {
      const names = res.body.map(o => o.name)
      expected.forEach(name => {
        expect(names).to.include(name)
      })
    })
  })

  it('oil price per drop is correctly calculated', () => {
    cy.request('/api/oils').then(res => {
      res.body.forEach(oil => {
        const ppd = oil.bottle_price / oil.drop_count
        expect(ppd).to.be.a('number')
        expect(ppd).to.be.gte(0)
        expect(ppd).to.be.lte(300) // sanity check: some premium oils can cost >100 per drop
      })
    })
  })

  it('drop counts match known volume standards', () => {
    // Standard doTERRA volumes: 5ml=93, 10ml=186, 15ml=280
    const validDropCounts = [46, 93, 160, 186, 280, 2146]
    cy.request('/api/oils').then(res => {
      const counts = new Set(res.body.map(o => o.drop_count))
      // At least some should match standard volumes
      const matching = [...counts].filter(c => validDropCounts.includes(c))
      expect(matching.length).to.be.gte(1)
    })
  })
})

describe('Recipe Data Integrity', () => {
  it('all recipes have valid structure', () => {
    cy.request('/api/recipes').then(res => {
      expect(res.body.length).to.be.gte(1)
      res.body.forEach(recipe => {
        expect(recipe).to.have.property('id')
        expect(recipe).to.have.property('name').and.not.be.empty
        expect(recipe).to.have.property('ingredients').and.be.an('array')
        recipe.ingredients.forEach(ing => {
          expect(ing).to.have.property('oil_name').and.not.be.empty
          expect(ing).to.have.property('drops').and.be.a('number').and.be.gt(0)
        })
      })
    })
  })

  it('recipes reference existing oils', () => {
    cy.request('/api/oils').then(oilRes => {
      const oilNames = new Set(oilRes.body.map(o => o.name))
      cy.request('/api/recipes').then(recipeRes => {
        let totalMissing = 0
        recipeRes.body.forEach(recipe => {
          recipe.ingredients.forEach(ing => {
            if (!oilNames.has(ing.oil_name)) totalMissing++
          })
        })
        // Allow some missing (discontinued oils), but not too many
        const totalIngs = recipeRes.body.reduce((s, r) => s + r.ingredients.length, 0)
        const missingRate = totalMissing / totalIngs
        expect(missingRate).to.be.lt(0.2) // less than 20% missing
      })
    })
  })

  it('no duplicate recipe names', () => {
    cy.request('/api/recipes').then(res => {
      const names = res.body.map(r => r.name)
      const unique = new Set(names)
      // Allow some duplicates but flag if many
      const dupRate = (names.length - unique.size) / names.length
      expect(dupRate).to.be.lt(0.1) // less than 10% duplicates
    })
  })

  it('recipe costs are calculable', () => {
    cy.request('/api/oils').then(oilRes => {
      const oilPrices = {}
      oilRes.body.forEach(o => {
        oilPrices[o.name] = o.bottle_price / o.drop_count
      })
      cy.request('/api/recipes').then(recipeRes => {
        recipeRes.body.slice(0, 20).forEach(recipe => {
          let cost = 0
          recipe.ingredients.forEach(ing => {
            cost += (oilPrices[ing.oil_name] || 0) * ing.drops
          })
          expect(cost).to.be.a('number')
          expect(cost).to.be.gte(0)
        })
      })
    })
  })
})
