//! Production planning example
//!
//! A company produces two products: Product A and Product B.
//! Each product requires different amounts of machine time and labor.
//! 
//! Constraints:
//! - Machine time: Product A needs 2 hours, Product B needs 3 hours. Total: 100 hours available
//! - Labor time: Product A needs 1 hour, Product B needs 2 hours. Total: 80 hours available  
//! - Material: Product A needs 1 unit, Product B needs 1 unit. Total: 50 units available
//! - Product A demand: at least 10 units
//!
//! Objective: Maximize profit (Product A: $30/unit, Product B: $40/unit)

use ockham::prelude::*;

fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    println!("=== Production Planning Example ===\n");

    let problem = ProblemBuilder::new()
        .name("Production Planning")
        .add_non_negative_variable("product_a")?
        .add_non_negative_variable("product_b")?
        // Machine time constraint: 2*A + 3*B <= 100
        .add_named_constraint(
            "machine_time",
            vec![2.0, 3.0],
            ConstraintType::LessEqual,
            100.0,
        )?
        // Labor time constraint: 1*A + 2*B <= 80
        .add_named_constraint(
            "labor_time", 
            vec![1.0, 2.0],
            ConstraintType::LessEqual,
            80.0,
        )?
        // Material constraint: 1*A + 1*B <= 50
        .add_named_constraint(
            "material",
            vec![1.0, 1.0],
            ConstraintType::LessEqual,
            50.0,
        )?
        // Demand constraint: A >= 10
        .add_named_constraint(
            "min_product_a",
            vec![1.0, 0.0],
            ConstraintType::GreaterEqual,
            10.0,
        )?
        // Objective: maximize 30*A + 40*B
        .named_objective(
            "profit",
            vec![30.0, 40.0],
            ObjectiveType::Maximize,
        )?
        .build()?;

    println!("Problem:");
    println!("{}", problem);

    // Apply presolve
    println!("\nApplying presolve...");
    let presolve = Presolve::new();
    let presolve_result = presolve.apply(&problem)?;
    println!("{}", presolve_result.statistics);

    let problem_to_solve = if presolve_result.statistics.presolved_variables < problem.num_variables() {
        println!("Using presolved problem");
        &presolve_result.problem
    } else {
        println!("No presolve improvements found, using original problem");
        &problem
    };

    // Apply scaling
    println!("\nApplying scaling...");
    let scaling = Scaling::new(ScalingMethod::Equilibration);
    let scaling_result = scaling.apply(problem_to_solve)?;
    println!("{}", scaling_result.statistics);

    // Solve with custom configuration
    let config = SolverConfig::new()
        .with_max_iterations(10000)
        .with_optimality_tolerance(1e-9)
        .verbose();

    println!("\nSolving...");
    let mut solver = SimplexSolver::new().with_pivot_rule(PivotRule::MostNegative);
    let solution = match solver.solve_with_config(&scaling_result.problem, &config) {
        Ok(sol) => sol,
        Err(e) => {
            println!("Solver error: {} - simplex implementation needs fixes", e);
            return Ok(());
        }
    };

    // Unscale the solution if needed
    let final_solution = if scaling_result.statistics.method != ScalingMethod::None {
        let unscaled_vars = scaling.unscale_solution(&solution.variables, &scaling_result.column_scales);
        Solution::optimal(unscaled_vars, solution.objective_value, solution.statistics)
    } else {
        solution
    };

    println!("\nSolution:");
    println!("{}", final_solution);

    if final_solution.is_optimal() {
        let product_a = final_solution.variables[0];
        let product_b = final_solution.variables[1];
        
        println!("\nProduction plan:");
        println!("  Product A: {:.2} units", product_a);
        println!("  Product B: {:.2} units", product_b);
        println!("  Total profit: ${:.2}", final_solution.objective_value);
        
        println!("\nResource utilization:");
        println!("  Machine time: {:.1}/{:.1} hours ({:.1}%)", 
                 2.0*product_a + 3.0*product_b, 100.0,
                 (2.0*product_a + 3.0*product_b) / 100.0 * 100.0);
        println!("  Labor time: {:.1}/{:.1} hours ({:.1}%)", 
                 1.0*product_a + 2.0*product_b, 80.0,
                 (1.0*product_a + 2.0*product_b) / 80.0 * 100.0);
        println!("  Material: {:.1}/{:.1} units ({:.1}%)", 
                 product_a + product_b, 50.0,
                 (product_a + product_b) / 50.0 * 100.0);
    }

    Ok(())
}