use entropyk_components::port::{Connected, FluidId, Port};
use entropyk_components::state_machine::CircuitId;
use entropyk_components::{
    Component, ComponentError, ConnectedPort, JacobianBuilder, MchxCondenserCoil, Polynomial2D,
    ResidualVector, ScrewEconomizerCompressor, ScrewPerformanceCurves, StateSlice,
};
use entropyk_core::{Enthalpy, MassFlow, Power, Pressure};
use entropyk_solver::inverse::{
    BoundedVariable, BoundedVariableId, ComponentOutput, Constraint, ConstraintId,
};
use entropyk_solver::system::System;

#[allow(dead_code)] // Convenience alias kept for readability in this fixture.
type CP = Port<Connected>;

fn make_port(fluid: &str, p_bar: f64, h_kj_kg: f64) -> ConnectedPort {
    let a = Port::new(
        FluidId::new(fluid),
        Pressure::from_bar(p_bar),
        Enthalpy::from_joules_per_kg(h_kj_kg * 1000.0),
    );
    let b = Port::new(
        FluidId::new(fluid),
        Pressure::from_bar(p_bar),
        Enthalpy::from_joules_per_kg(h_kj_kg * 1000.0),
    );
    a.connect(b).expect("port connection ok").0
}

fn make_screw_curves() -> ScrewPerformanceCurves {
    ScrewPerformanceCurves::with_fixed_eco_fraction(
        Polynomial2D::bilinear(1.20, 0.003, -0.002, 0.000_01),
        Polynomial2D::bilinear(55_000.0, 200.0, -300.0, 0.5),
        0.12,
    )
}

struct Mock {
    n: usize,
    #[allow(dead_code)] // Stored for fixture completeness; not asserted in this test.
    circuit_id: CircuitId,
}

impl Mock {
    fn new(n: usize, circuit: u16) -> Self {
        Self {
            n,
            circuit_id: CircuitId(circuit),
        }
    }
}

impl Component for Mock {
    fn compute_residuals(
        &self,
        _state: &StateSlice,
        residuals: &mut ResidualVector,
    ) -> Result<(), ComponentError> {
        for r in residuals.iter_mut().take(self.n) {
            *r = 0.0;
        }
        Ok(())
    }

    fn jacobian_entries(
        &self,
        _state: &StateSlice,
        _jacobian: &mut JacobianBuilder,
    ) -> Result<(), ComponentError> {
        Ok(())
    }

    fn n_equations(&self) -> usize {
        self.n
    }

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

    fn port_mass_flows(&self, _state: &StateSlice) -> Result<Vec<MassFlow>, ComponentError> {
        Ok(vec![MassFlow::from_kg_per_s(1.0)])
    }

    fn energy_transfers(&self, _state: &StateSlice) -> Option<(Power, Power)> {
        Some((Power::from_watts(0.0), Power::from_watts(0.0)))
    }
}

#[test]
fn test_real_cycle_inverse_control_integration() {
    let mut sys = System::new();

    // 1. Create components
    let comp_suc = make_port("R134a", 3.2, 400.0);
    let comp_dis = make_port("R134a", 12.8, 440.0);
    let comp_eco = make_port("R134a", 6.4, 260.0);

    let comp = ScrewEconomizerCompressor::new(
        make_screw_curves(),
        "R134a",
        50.0,
        0.92,
        comp_suc,
        comp_dis,
        comp_eco,
    )
    .unwrap();

    let coil = MchxCondenserCoil::for_35c_ambient(15_000.0, 0);
    // CM1.4 DoF balance for a 4-edge series cycle (state_len=10: 1 branch + 8 P,h + 1 eco embed):
    //   ScrewEco (6 eqs) + MchxCoil (2 eqs with same_branch_m) + exv (1) + evap (1) = 10 ✓
    let exv = Mock::new(1, 0); // Expansion Valve — 1 equation (simplified pass-through)
    let evap = Mock::new(1, 0); // Evaporator — 1 equation (simplified pass-through)

    // 2. Add components to system
    let comp_node = sys
        .add_component_to_circuit(Box::new(comp), CircuitId::ZERO)
        .unwrap();
    let coil_node = sys
        .add_component_to_circuit(Box::new(coil), CircuitId::ZERO)
        .unwrap();
    let exv_node = sys
        .add_component_to_circuit(Box::new(exv), CircuitId::ZERO)
        .unwrap();
    let evap_node = sys
        .add_component_to_circuit(Box::new(evap), CircuitId::ZERO)
        .unwrap();

    sys.register_component_name("compressor", comp_node);
    sys.register_component_name("condenser", coil_node);
    sys.register_component_name("expansion_valve", exv_node);
    sys.register_component_name("evaporator", evap_node);

    // 3. Connect components
    sys.add_edge(comp_node, coil_node).unwrap();
    sys.add_edge(coil_node, exv_node).unwrap();
    sys.add_edge(exv_node, evap_node).unwrap();
    sys.add_edge(evap_node, comp_node).unwrap();

    // 4. Add Inverse Control Elements (Constraints and BoundedVariables)
    // Constraint 1: Superheat at evaporator = 5K
    sys.add_constraint(Constraint::new(
        ConstraintId::new("superheat_control"),
        ComponentOutput::Superheat {
            component_id: "evaporator".to_string(),
        },
        5.0,
    ))
    .unwrap();

    // Constraint 2: Capacity at compressor = 50000 W
    sys.add_constraint(Constraint::new(
        ConstraintId::new("capacity_control"),
        ComponentOutput::Capacity {
            component_id: "compressor".to_string(),
        },
        50000.0,
    ))
    .unwrap();

    // Control 1: Valve Opening
    let bv_valve = BoundedVariable::with_component(
        BoundedVariableId::new("valve_opening"),
        "expansion_valve",
        0.5,
        0.0,
        1.0,
    )
    .unwrap();
    sys.add_bounded_variable(bv_valve).unwrap();

    // Control 2: Compressor Speed
    let bv_comp = BoundedVariable::with_component(
        BoundedVariableId::new("compressor_speed"),
        "compressor",
        0.7,
        0.3,
        1.0,
    )
    .unwrap();
    sys.add_bounded_variable(bv_comp).unwrap();

    // Link constraints to controls
    sys.link_constraint_to_control(
        &ConstraintId::new("superheat_control"),
        &BoundedVariableId::new("valve_opening"),
    )
    .unwrap();

    sys.link_constraint_to_control(
        &ConstraintId::new("capacity_control"),
        &BoundedVariableId::new("compressor_speed"),
    )
    .unwrap();

    // 5. Finalize the system
    sys.finalize().unwrap();

    // Verify system state size and degrees of freedom
    assert_eq!(sys.constraint_count(), 2);
    assert_eq!(sys.bounded_variable_count(), 2);

    // Validate DoF
    sys.validate_inverse_control_dof()
        .expect("System should be balanced for inverse control");

    // Evaluate the total system residual and jacobian capability
    let state_len = sys.state_vector_len();
    assert!(state_len > 0, "System should have state variables");

    // Create mock state and control values
    let state = vec![400_000.0; state_len];
    let control_values = vec![0.5, 0.7]; // Valve, Compressor speeds

    let mut residuals = vec![0.0; state_len + 2];

    // Evaluate constraints
    let measured = sys.extract_constraint_values_with_controls(&state, &control_values);
    let count = sys
        .compute_constraint_residuals(&state, &mut residuals[state_len..], &measured)
        .expect("constraint residuals should compute");

    assert_eq!(count, 2, "Should have computed 2 constraint residuals");

    // Evaluate jacobian
    let jacobian_entries = sys.compute_inverse_control_jacobian(&state, state_len, &control_values);

    assert!(
        !jacobian_entries.is_empty(),
        "Jacobian should have entries for inverse control"
    );

    println!("System integration with inverse control successful!");
}
