// plugin/nc-calc-server/test/product-convert.test.js
/* eslint-disable no-undef -- vitest globals (vi) are available */
import { describe, it, expect, beforeAll } from 'vitest'
import nc from '../../../../nc-frontend/src/core/nc-util.js'
import {
	updatePacket,
	unitAmount,
	amountStock1ToStock2,
	amountStock2ToStock1,
	amountStock1ToDesign1,
	amountStock1ToDesign2,
	amountDesign1ToStock1,
	amountDesign2ToStock1,
	calculatePacketSize,
	getUnitCount,
	setState
} from '../convert/product-convert.js'
import testProducts from './test-data.json' with { type: 'json' }

describe('product-convert', () => {
	beforeAll(() => {
		setState(nc)
	})
	testProducts.forEach((testProduct) => {
		const expected = testProduct.expected
		describe(`${testProduct.product_id}`, () => {
			describe('amountStock1ToStock2', () => {
				it('should convert from stock unit 1 to stock unit 2', () => {
					const result = amountStock1ToStock2(testProduct, expected.stock1ToStock2.input)
					expect(result).toBeCloseTo(expected.stock1ToStock2.output, 3)
				})
			})
			describe('amountStock2ToStock1', () => {
				it('should convert from stock unit 2 to stock unit 1', () => {
					const result = amountStock2ToStock1(testProduct, expected.stock2ToStock1.input)
					expect(result).toBeCloseTo(expected.stock2ToStock1.output, 3)
				})
			})
			describe('amountDesign1ToStock1', () => {
				it('should convert from design unit 1 to stock unit 1', () => {
					const result = amountDesign1ToStock1(testProduct, expected.design1ToStock1.input)
					expect(result).toBeCloseTo(expected.design1ToStock1.output, 3)
				})
			})
			describe('amountStock1ToDesign1', () => {
				it('should convert from stock unit 1 to design unit 1', () => {
					const result = amountStock1ToDesign1(testProduct, expected.stock1ToDesign1.input)
					expect(result).toBeCloseTo(expected.stock1ToDesign1.output, 3)
				})
			})
			describe('amountStock1ToDesign2', () => {
				it('should convert from stock unit 1 to design unit 2', () => {
					const result = amountStock1ToDesign2(testProduct, expected.stock1ToDesign2.input)
					expect(result).toBeCloseTo(expected.stock1ToDesign2.output, 3)
				})
			})
			describe('amountDesign2ToStock1', () => {
				it('should convert from design unit 2 to stock unit 1', () => {
					const result = amountDesign2ToStock1(testProduct, expected.design2ToStock1.input)
					expect(result).toBeCloseTo(expected.design2ToStock1.output, 3)
				})
			})
		})
	})
	// Tests that only need to run once (not product-specific)
	const testProduct = testProducts[testProducts.length - 1]
	const testPacket = {
		row: { pr: testProduct },
		stock_amount_1: 1000,
		stock_amount_2: 10,
		design_amount_1: 1000,
		design_amount_2: 1000,
		packet_count: 10,
		save_amount: 100,
		stock_amount_full_packet_1: 100,
		stock_amount_full_packet_2: 1,
		stock_amount_last_packet_1: 0,
		stock_amount_last_packet_2: 0,
		design_amount_full_packet_1: 100,
		design_amount_full_packet_2: 100,
		design_amount_last_packet_1: 0,
		design_amount_last_packet_2: 0
	}
	describe('edge cases and error handling', () => {
		describe('amountStock1ToStock2', () => {
			it('should return 0 when product is null', () => {
				const result = amountStock1ToStock2(null, 100)
				expect(result).toBe(0)
			})
			it('should return 0 when unit_multiplier is 0', () => {
				const pr = { ...testProduct, unit_multiplier: 0 }
				const result = amountStock1ToStock2(pr, 100)
				expect(result).toBe(0)
			})
			it('should return same amount when unit_multiplier is 1', () => {
				const pr = { ...testProduct, unit_multiplier: 1 }
				const result = amountStock1ToStock2(pr, 100)
				expect(result).toBe(100)
			})
			it('should return 1/amount when unit_multiplier is -1', () => {
				const pr = { ...testProduct, unit_multiplier: -1 }
				const result = amountStock1ToStock2(pr, 3)
				expect(result).toBe(0.333333)
			})
		})
		describe('amountStock2ToStock1', () => {
			it('should return 0 when product is null', () => {
				const result = amountStock2ToStock1(null, 10000)
				expect(result).toBe(0)
			})
			it('should return 0 when unit_multiplier is 0', () => {
				const pr = { ...testProduct, unit_multiplier: 0 }
				const result = amountStock2ToStock1(pr, 10000)
				expect(result).toBe(0)
			})
			it('should return same amount when unit_multiplier is 1', () => {
				const pr = { ...testProduct, unit_multiplier: 1 }
				const result = amountStock2ToStock1(pr, 100)
				expect(result).toBe(100)
			})
			it('should return 1/amount when unit_multiplier is -1', () => {
				const pr = { ...testProduct, unit_multiplier: -1 }
				const result = amountStock2ToStock1(pr, 3)
				expect(result).toBe(0.333333)
			})
		})
		describe('calculatePacketSize', () => {
			it('should set zeros when packet_count is 0', () => {
				const packet = { ...testPacket, packet_count: 0, stock_amount_1: 1000 }
				calculatePacketSize(packet, 'stock_amount_1')
				expect(packet.stock_amount_full_packet_1).toBe(0)
				expect(packet.stock_amount_last_packet_1).toBe(0)
			})
			it('should set zeros when stock_amount_1 is 0', () => {
				const packet = { ...testPacket, stock_amount_1: 0 }
				calculatePacketSize(packet, 'stock_amount_1')
				expect(packet.stock_amount_full_packet_1).toBe(0)
				expect(packet.stock_amount_last_packet_1).toBe(0)
			})
			it('should set full_packet_size as stock_amount_1 when stock_amount_1 < 1', () => {
				const packet = { ...testPacket, packet_count: 1, stock_amount_1: 0.5 }
				calculatePacketSize(packet, 'stock_amount_1')
				expect(packet.stock_amount_full_packet_1).toBe(0.5)
				expect(packet.stock_amount_last_packet_1).toBe(0)
			})
			it('should calculate packet sizes with evenly divisible total (ceil formula)', () => {
				const packet = { ...testPacket, stock_amount_1: 1000, packet_count: 10 }
				calculatePacketSize(packet, 'stock_amount_1')
				// ceil(1000/10) = 100, last = 1000 - 100*9 = 100, but last >= full so last = 0
				expect(packet.stock_amount_full_packet_1).toBe(100)
				expect(packet.stock_amount_last_packet_1).toBe(0)
			})
			it('should calculate packet sizes ensuring last < full (15.3, 4 packets)', () => {
				const packet = { ...testPacket, stock_amount_1: 15.3, packet_count: 4 }
				calculatePacketSize(packet, 'stock_amount_1')
				// ceil(15.3/4) = 4, last = 15.3 - 4*3 = 3.3, 3.3 < 4 ✓
				expect(packet.stock_amount_full_packet_1).toBe(4)
				expect(packet.stock_amount_last_packet_1).toBeCloseTo(3.3, 3)
			})
			it('should calculate packet sizes ensuring last < full (1000, 8 packets)', () => {
				const packet = { ...testPacket, stock_amount_1: 1000, packet_count: 8 }
				calculatePacketSize(packet, 'stock_amount_1')
				// ceil(1000/8) = 125, last = 1000 - 125*7 = 125, but last >= full so last = 0
				expect(packet.stock_amount_full_packet_1).toBe(125)
				expect(packet.stock_amount_last_packet_1).toBe(0)
			})
			it('should calculate packet sizes ensuring last < full (999, 8 packets)', () => {
				const packet = { ...testPacket, stock_amount_1: 999, packet_count: 8 }
				calculatePacketSize(packet, 'stock_amount_1')
				// ceil(999/8) = 125, last = 999 - 125*7 = 124, 124 < 125 ✓
				expect(packet.stock_amount_full_packet_1).toBe(125)
				expect(packet.stock_amount_last_packet_1).toBe(124)
			})
		})
		describe('unitAmount - empty changedField', () => {
			it('should handle empty changedField without error', () => {
				const packet = { ...testPacket, stock_amount_1: 500 }
				const consoleErrorSpy = vi.spyOn(console, 'error')
				unitAmount('', packet)
				expect(consoleErrorSpy).not.toHaveBeenCalled()
			})
		})
		describe('unitAmount - invalid changedField', () => {
			it('should log error for invalid changedField', () => {
				const packet = { ...testPacket }
				const consoleErrorSpy = vi.spyOn(console, 'error')
				unitAmount('invalid_field', packet)
				expect(consoleErrorSpy).toHaveBeenCalledWith(
					"unitAmount: changedField 'invalid_field' is not handled. Valid fields are: stock_amount_1, stock_amount_2, stock_amount_full_packet_1, stock_amount_full_packet_2, stock_amount_last_packet_1, stock_amount_last_packet_2, design_amount_1, design_amount_2, design_amount_full_packet_1, design_amount_full_packet_2, design_amount_last_packet_1, design_amount_last_packet_2"
				)
			})
		})
		describe('unitAmount - null product', () => {
			it('should handle null product gracefully', () => {
				const packet = { ...testPacket, row: null }
				unitAmount('stock_amount_1', packet)
				expect(packet.stock_amount_1).toBe(1000)
			})
			it('should handle product with null pr', () => {
				const packet = { ...testPacket, row: {} }
				unitAmount('stock_amount_1', packet)
				expect(packet.stock_amount_1).toBe(1000)
			})
		})
		describe('updatePacket', () => {
			it('should convert field to number when not excluded', () => {
				const packet = { ...testPacket, stock_amount_1: '500' }
				updatePacket('stock_amount_1', packet, false)
				expect(packet.stock_amount_1).toBe(500)
			})
			it('should not convert field to number when excluded', () => {
				const packet = { ...testPacket, stock_amount_2: 5 }
				updatePacket('stock_amount_2', packet, true)
				expect(packet.stock_amount_2).toBe(5)
			})
			it('should call unitAmount then calculatePacketSize', () => {
				const packet = { ...testPacket, stock_amount_1: 500, packet_count: 5 }
				updatePacket('stock_amount_1', packet, false)
				expect(packet.stock_amount_full_packet_1).toBe(100)
				expect(packet.stock_amount_last_packet_1).toBe(0)
			})
			it('should handle excluded fields that are never overwritten', () => {
				const excludedFieldArray = [
					'stock_amount_2',
					'design_amount_1',
					'design_amount_2',
					'stock_amount_last_packet_1',
					'design_amount_full_packet_1',
					'design_amount_full_packet_2',
					'design_amount_last_packet_1',
					'design_amount_last_packet_2'
				]
				excludedFieldArray.forEach(field => {
					const packet = { ...testPacket }
					packet[field] = 123
					updatePacket(field, packet, false)
					expect(packet[field]).toBe(123)
				})
			})
		})
		describe('getUnitCount', () => {
			it('should return 0 when product is null', () => {
				const result = getUnitCount(null)
				expect(result.unitCount).toBe(0)
				expect(result.showUnit2).toBe(false)
				expect(result.showDesignUnit).toBe(false)
				expect(result.showDesignUnit2).toBe(false)
				expect(result.showDesignMultiplier).toBe(false)
				expect(result.showStockDesignMultiplier).toBe(false)
			})
			it('should show unit2 when stock_unit_2 differs from stock_unit', () => {
				const result = getUnitCount(testProduct)
				expect(result.showUnit2).toBe(true)
			})
			it('should not show unit2 when stock_unit_2 is same as stock_unit', () => {
				const pr = { ...testProduct, json_data: { ...testProduct.json_data, stock_unit_2: 'pcs' } }
				const result = getUnitCount(pr)
				expect(result.showUnit2).toBe(false)
			})
			it('should not show unit2 when stock_unit_2 is empty', () => {
				const pr = { ...testProduct, json_data: { ...testProduct.json_data, stock_unit_2: '' } }
				const result = getUnitCount(pr)
				expect(result.showUnit2).toBe(false)
			})
			it('should not show design unit when it is same as stock unit', () => {
				const result = getUnitCount(testProduct)
				expect(result.showDesignUnit).toBe(false)
			})
			it('should calculate correct unitCount', () => {
				const result = getUnitCount(testProduct)
				expect(result.unitCount).toBe(3)
			})
			it('should show stock design multiplier when positive', () => {
				const result = getUnitCount(testProduct)
				expect(result.showStockDesignMultiplier).toBe(true)
			})
			it('should not show stock design multiplier when not positive', () => {
				const pr = { ...testProduct, json_data: { ...testProduct.json_data, unit_design_unit_multiplier: 0 } }
				const result = getUnitCount(pr)
				expect(result.showStockDesignMultiplier).toBe(false)
			})
		})
	})
	describe('nc object injection', () => {
		it('should use injected nc.round', () => {
			const pr = { unit_multiplier: 3 }
			const result = amountStock1ToStock2(pr, 10)
			expect(result).toBeCloseTo(3.33333333333333, 3)
		})
	})
})
