import { test } from 'node:test';
import assert from 'node:assert';
import * as musica from '../../index.js';

const { types, reactionTypes, Mechanism } = musica.mechanismConfiguration;
const { Species, PhaseSpecies, Phase, ReactionComponent } = types;

// ===== Species =====
const A = new Species({
	name: 'A',
	molecular_weight: 0.04607,
	'absolute tolerance': 1.0e-30,
});
const B = new Species({ name: 'B', constant_concentration: 1.0e19 });
const C = new Species({ name: 'C', constant_mixing_ratio: 1.0e-20 });
const M = new Species({ name: 'M', is_third_body: true });
const H2O2 = new Species({
	name: 'H2O2',
	molecular_weight: 0.0340147,
	'absolute tolerance': 1.0e-10,
});
const ethanol = new Species({
	name: 'ethanol',
	molecular_weight: 0.04607,
	'absolute tolerance': 1.0e-20,
});
const H2O = new Species({ name: 'H2O', molecular_weight: 0.01801 });

// ===== Phases =====
const gas = new Phase({
	name: 'gas',
	species: [
		new PhaseSpecies({ name: A.name, diffusion_coefficient: 2.1e-5 }),
		B,
		C,
		new PhaseSpecies({ name: ethanol.name, diffusion_coefficient: 2.1e-5 }),
		new PhaseSpecies({ name: H2O2.name, diffusion_coefficient: 2.1e-5 }),
	],
});

// ===== Reactions =====
const my_emission = new reactionTypes.Emission({
	name: 'my emission',
	scaling_factor: 12.3,
	gas_phase: 'gas',
	products: [new ReactionComponent({ species_name: 'B' })],
});
const my_first_order_loss = new reactionTypes.FirstOrderLoss({
	name: 'my first order loss',
	scaling_factor: 12.3,
	gas_phase: 'gas',
	reactants: [new ReactionComponent({ species_name: 'C' })],
});
const my_photolysis = new reactionTypes.Photolysis({
	name: 'photo B',
	scaling_factor: 12.3,
	gas_phase: 'gas',
	reactants: [new ReactionComponent({ species_name: 'B' })],
	products: [new ReactionComponent({ species_name: 'C' })],
});
// BUG: gas_phase_species should be a ReactionComponent, but is a string in the full_config expected output
const my_surface = new reactionTypes.Surface({
	name: 'my surface',
	reaction_probability: 2.0e-2,
	gas_phase: 'gas',
	gas_phase_species: new ReactionComponent({ species_name: 'A' }),
	gas_phase_products: [
		new ReactionComponent({ species_name: 'B' }),
		new ReactionComponent({ species_name: 'C' }),
	],
});
const my_troe = new reactionTypes.Troe({
	name: 'my troe',
	k0_A: 1.2e-12,
	k0_B: 167,
	k0_C: 3,
	kinf_A: 136,
	kinf_B: 5,
	kinf_C: 24,
	Fc: 0.9,
	N: 0.8,
	gas_phase: 'gas',
	reactants: [
		new ReactionComponent({ species_name: 'B' }),
		new ReactionComponent({ species_name: 'M' }),
	],
	products: [new ReactionComponent({ species_name: 'C' })],
});
const my_tca = new reactionTypes.TernaryChemicalActivation({
	name: 'my ternary chemical activation',
	gas_phase: 'gas',
	k0_A: 32.1,
	k0_B: -2.3,
	k0_C: 102.3,
	kinf_A: 63.4,
	kinf_B: -1.3,
	kinf_C: 908.5,
	Fc: 1.3,
	N: 32.1,
	reactants: [
		new ReactionComponent({ species_name: 'bar' }),
		new ReactionComponent({ species_name: 'baz' }),
	],
	products: [
		new ReactionComponent({ species_name: 'bar', coefficient: 0.5 }),
		new ReactionComponent({ species_name: 'foo', coefficient: 0.3 }),
	],
});
const my_branched = new reactionTypes.Branched({
	name: 'my branched',
	X: 1.2e-4,
	Y: 167,
	a0: 0.15,
	n: 9,
	gas_phase: 'gas',
	reactants: [new ReactionComponent({ species_name: 'A' })],
	alkoxy_products: [new ReactionComponent({ species_name: 'B' })],
	nitrate_products: [new ReactionComponent({ species_name: 'C' })],
});
const my_tunneling = new reactionTypes.Tunneling({
	name: 'my tunneling',
	A: 123.45,
	B: 1200.0,
	C: 1.0e8,
	gas_phase: 'gas',
	reactants: [new ReactionComponent({ species_name: 'B' })],
	products: [new ReactionComponent({ species_name: 'C' })],
});
const my_arrhenius = new reactionTypes.Arrhenius({
	name: 'my arrhenius',
	A: 32.1,
	B: -2.3,
	C: 102.3,
	D: 63.4,
	E: -1.3,
	gas_phase: 'gas',
	reactants: [new ReactionComponent({ species_name: 'B' })],
	products: [new ReactionComponent({ species_name: 'C' })],
});
const my_arrhenius2 = new reactionTypes.Arrhenius({
	name: 'my other arrhenius',
	A: 29.3,
	B: -1.5,
	Ea: 101.2,
	D: 82.6,
	E: -0.98,
	gas_phase: 'gas',
	reactants: [new ReactionComponent({ species_name: 'A' })],
	products: [new ReactionComponent({ species_name: 'B', coefficient: 1.2 })],
});
const my_ud = new reactionTypes.UserDefined({
	name: 'my user defined',
	gas_phase: 'gas',
	scaling_factor: 12.3,
	reactants: [
		new ReactionComponent({ species_name: 'A' }),
		new ReactionComponent({ species_name: 'B' }),
	],
	products: [new ReactionComponent({ species_name: 'C', coefficient: 1.3 })],
});
const my_ts = new reactionTypes.TaylorSeries({
	name: 'my taylor series',
	gas_phase: 'gas',
	A: 12.3,
	B: -1.5,
	C: 1.0e-6,
	D: 340.0,
	E: 0.00032,
	taylor_coefficients: [1.0, 0.1, -0.01],
	reactants: [new ReactionComponent({ species_name: 'B' })],
	products: [new ReactionComponent({ species_name: 'C' })],
});

// ===== Mechanism =====
const full_config_mechanism = new Mechanism({
	name: 'Full Configuration',
	version: '1.0.0',
	species: [A, B, C, M, H2O2, ethanol, H2O],
	phases: [gas],
	reactions: [
		my_emission,
		my_first_order_loss,
		my_photolysis,
		my_surface,
		my_troe,
		my_tca,
		my_branched,
		my_tunneling,
		my_arrhenius,
		my_arrhenius2,
		my_ud,
		my_ts,
	],
});

const expected = {
	version: '1.0.0',
	name: 'Full Configuration',
	species: [
		{
			name: 'A',
			'molecular weight [kg mol-1]': 0.04607,
			'__absolute tolerance': 1.0e-30,
		},
		{
			name: 'B',
			'constant concentration [mol m-3]': 1.0e19,
		},
		{
			name: 'C',
			'constant mixing ratio [mol mol-1]': 1.0e-20,
		},
		{
			name: 'M',
			'is third body': true,
		},
		{
			name: 'H2O2',
			'molecular weight [kg mol-1]': 0.0340147,
			'__absolute tolerance': 1.0e-10,
		},
		{
			name: 'ethanol',
			'molecular weight [kg mol-1]': 0.04607,
			'__absolute tolerance': 1.0e-20,
		},
		{
			name: 'H2O',
			'molecular weight [kg mol-1]': 0.01801,
		},
	],
	phases: [
		{
			name: 'gas',
			species: [
				{
					name: 'A',
					'diffusion coefficient [m2 s-1]': 2.1e-5,
				},
				{ name: 'B' },
				{ name: 'C' },
				{
					name: 'ethanol',
					'diffusion coefficient [m2 s-1]': 2.1e-5,
				},
				{
					name: 'H2O2',
					'diffusion coefficient [m2 s-1]': 2.1e-5,
				},
			],
		},
	],
	reactions: [
		{
			type: 'EMISSION',
			name: 'my emission',
			'scaling factor': 12.3,
			'gas phase': 'gas',
			products: [
				{
					'species name': 'B',
					coefficient: 1,
				},
			],
		},
		{
			type: 'FIRST_ORDER_LOSS',
			name: 'my first order loss',
			'scaling factor': 12.3,
			'gas phase': 'gas',
			reactants: [
				{
					'species name': 'C',
					coefficient: 1,
				},
			],
		},
		{
			type: 'PHOTOLYSIS',
			name: 'photo B',
			'scaling factor': 12.3,
			'gas phase': 'gas',
			reactants: [
				{
					'species name': 'B',
					coefficient: 1,
				},
			],
			products: [
				{
					'species name': 'C',
					coefficient: 1,
				},
			],
		},
		{
			type: 'SURFACE',
			name: 'my surface',
			'reaction probability': 2.0e-2,
			'gas phase': 'gas',
			'gas-phase species': 'A',
			'gas-phase products': [
				{
					'species name': 'B',
					coefficient: 1,
				},
				{
					'species name': 'C',
					coefficient: 1,
				},
			],
		},
		{
			type: 'TROE',
			name: 'my troe',
			k0_A: 1.2e-12,
			k0_B: 167,
			k0_C: 3,
			kinf_A: 136,
			kinf_B: 5,
			kinf_C: 24,
			Fc: 0.9,
			N: 0.8,
			'gas phase': 'gas',
			reactants: [
				{
					'species name': 'B',
					coefficient: 1,
				},
				{
					'species name': 'M',
					coefficient: 1,
				},
			],
			products: [
				{
					'species name': 'C',
					coefficient: 1,
				},
			],
		},
		{
			type: 'TERNARY_CHEMICAL_ACTIVATION',
			name: 'my ternary chemical activation',
			'gas phase': 'gas',
			k0_A: 32.1,
			k0_B: -2.3,
			k0_C: 102.3,
			kinf_A: 63.4,
			kinf_B: -1.3,
			kinf_C: 908.5,
			Fc: 1.3,
			N: 32.1,
			reactants: [
				{
					'species name': 'bar',
					coefficient: 1,
				},
				{
					'species name': 'baz',
					coefficient: 1,
				},
			],
			products: [
				{
					'species name': 'bar',
					coefficient: 0.5,
				},
				{
					'species name': 'foo',
					coefficient: 0.3,
				},
			],
		},
		{
			type: 'BRANCHED_NO_RO2',
			name: 'my branched',
			X: 1.2e-4,
			Y: 167,
			a0: 0.15,
			n: 9,
			'gas phase': 'gas',
			reactants: [
				{
					'species name': 'A',
					coefficient: 1,
				},
			],
			'alkoxy products': [
				{
					'species name': 'B',
					coefficient: 1,
				},
			],
			'nitrate products': [
				{
					'species name': 'C',
					coefficient: 1,
				},
			],
		},
		{
			type: 'TUNNELING',
			name: 'my tunneling',
			A: 123.45,
			B: 1200.0,
			C: 1.0e8,
			'gas phase': 'gas',
			reactants: [
				{
					'species name': 'B',
					coefficient: 1,
				},
			],
			products: [
				{
					'species name': 'C',
					coefficient: 1,
				},
			],
		},
		{
			type: 'ARRHENIUS',
			name: 'my arrhenius',
			'gas phase': 'gas',
			A: 32.1,
			B: -2.3,
			C: 102.3,
			D: 63.4,
			E: -1.3,
			reactants: [
				{
					'species name': 'B',
					coefficient: 1,
				},
			],
			products: [
				{
					'species name': 'C',
					coefficient: 1,
				},
			],
		},
		{
			type: 'ARRHENIUS',
			name: 'my other arrhenius',
			A: 29.3,
			B: -1.5,
			Ea: 101.2,
			D: 82.6,
			E: -0.98,
			'gas phase': 'gas',
			reactants: [
				{
					'species name': 'A',
					coefficient: 1,
				},
			],
			products: [
				{
					'species name': 'B',
					coefficient: 1.2,
				},
			],
		},
		{
			type: 'USER_DEFINED',
			name: 'my user defined',
			'scaling factor': 12.3,
			'gas phase': 'gas',
			reactants: [
				{
					'species name': 'A',
					coefficient: 1,
				},
				{
					'species name': 'B',
					coefficient: 1,
				},
			],
			products: [
				{
					'species name': 'C',
					coefficient: 1.3,
				},
			],
		},
		{
			type: 'TAYLOR_SERIES',
			name: 'my taylor series',
			A: 12.3,
			B: -1.5,
			C: 1.0e-6,
			D: 340.0,
			E: 0.00032,
			'taylor coefficients': [1.0, 0.1, -0.01],
			'gas phase': 'gas',
			reactants: [
				{
					'species name': 'B',
					coefficient: 1,
				},
			],
			products: [
				{
					'species name': 'C',
					coefficient: 1,
				},
			],
		},
	],
};

/**
 * Recursively compare two objects (or arrays) for deep equality.
 * Arrays are compared as unordered collections.
 */
function deepEqual(a, b, path = '') {
	if (a === b) return true;
	if (typeof a !== typeof b) {
		return false;
	}
	if (a === null || b === null) {
		if (a !== b) {
			return false;
		}
		return true;
	}
	if (typeof a !== 'object') {
		return false;
	}

	// Arrays (unordered comparison)
	if (Array.isArray(a)) {
		if (!Array.isArray(b)) {
			return false;
		}
		if (a.length !== b.length) {
			return false;
		}
		// Track which elements in b have been matched
		const matched = new Array(b.length).fill(false);
		for (let i = 0; i < a.length; i++) {
			let found = false;
			for (let j = 0; j < b.length; j++) {
				if (!matched[j] && deepEqual(a[i], b[j], `${path}[${i}]`)) {
					matched[j] = true;
					found = true;
					break;
				}
			}
			if (!found) {
				console.log(`No match for element at ${path}[${i}]:`, a[i]);
				return false;
			}
		}
		return true;
	}

	// Objects
	const aKeys = Object.keys(a);
	const bKeys = Object.keys(b);
	if (aKeys.length !== bKeys.length) {
		return false;
	}
	for (const key of aKeys) {
		if (!b.hasOwnProperty(key)) {
			return false;
		}
		if (!deepEqual(a[key], b[key], path ? `${path}.${key}` : key))
			return false;
	}
	return true;
}

test('Full configuration export matches expected output', (t) => {
	const full_config_sanatized = JSON.parse(full_config_mechanism.getString());

	assert.ok(
		deepEqual(full_config_sanatized, expected, 'root'),
		'Full configuration should match expected output'
	);
});
