use entropyk_components::port::{FluidId, Port};
use entropyk_components::{Component, ComponentError, ConnectedPort, JacobianBuilder, StateSlice};
use entropyk_core::{Enthalpy, Pressure};
use entropyk_solver::solver::{NewtonConfig, Solver};
use entropyk_solver::system::System;

struct DummyComponent {
    ports: Vec<ConnectedPort>,
}

impl Component for DummyComponent {
    fn compute_residuals(
        &self,
        _state: &StateSlice,
        residuals: &mut entropyk_components::ResidualVector,
    ) -> Result<(), ComponentError> {
        residuals[0] = 0.0;
        residuals[1] = 0.0;
        Ok(())
    }

    fn jacobian_entries(
        &self,
        _state: &StateSlice,
        jacobian: &mut JacobianBuilder,
    ) -> Result<(), ComponentError> {
        jacobian.add_entry(0, 0, 1.0);
        jacobian.add_entry(1, 1, 1.0);
        Ok(())
    }

    fn n_equations(&self) -> usize {
        2
    }

    fn get_ports(&self) -> &[ConnectedPort] {
        &self.ports
    }
}

fn make_dummy_component() -> Box<dyn Component> {
    let inlet = Port::new(
        FluidId::new("R134a"),
        Pressure::from_pascals(100_000.0),
        Enthalpy::from_joules_per_kg(400_000.0),
    );
    let outlet = Port::new(
        FluidId::new("R134a"),
        Pressure::from_pascals(100_000.0),
        Enthalpy::from_joules_per_kg(400_000.0),
    );
    let (connected_inlet, connected_outlet) = inlet.connect(outlet).unwrap();
    let ports = vec![connected_inlet, connected_outlet];
    Box::new(DummyComponent { ports })
}

#[test]
fn test_simulation_metadata_outputs() {
    let mut sys = System::new();
    let n0 = sys.add_component(make_dummy_component());
    let n1 = sys.add_component(make_dummy_component());
    sys.add_edge_with_ports(n0, 1, n1, 0).unwrap();
    sys.add_edge_with_ports(n1, 1, n0, 0).unwrap();

    sys.finalize().unwrap();

    let input_hash = sys.input_hash();

    let mut solver = NewtonConfig {
        max_iterations: 5,
        ..Default::default()
    };
    let result = solver.solve(&mut sys).unwrap();

    assert!(result.is_converged());

    let metadata = result.metadata;
    assert_eq!(metadata.input_hash, input_hash);
    assert_eq!(metadata.solver_version, env!("CARGO_PKG_VERSION"));
    assert_eq!(metadata.fluid_backend_version, "0.1.0");
}
