use std::path::{Path, PathBuf};

use clap::Parser;
use gurobi::*;
use itertools::Itertools;

fn read_input(filename: impl AsRef<Path>) -> Vec<Vec<u8>> {
    let text = std::fs::read_to_string(filename).unwrap();

    let mut r = Vec::new();

    for line in text.lines().skip(1) {
        r.push(line.as_bytes().to_vec());
    }

    r
}

#[derive(Debug, Parser)]
struct Args {
    filename: PathBuf,
}

fn solve(input: &[Vec<u8>], d: usize) {
    let mut columns = Vec::new();

    for i in 0..input[0].len() {
        let mut nextfree: u8 = 0;
        let mut c = Vec::new();

        for l in 0..input.len() {
            if let Some(n) = input[0..l].iter().position(|r| r[i] == input[l][i]) {
                c.push(c[n]);
            } else {
                c.push(nextfree);
                nextfree += 1;
            }
        }

        columns.push(c);
    }

    let counts = columns.iter().counts();

    let env = Env::new("logfile.log").unwrap();

    let mut model = Model::new("model1", &env).unwrap();

    let mut variables = Vec::new();

    for (k, &c) in &counts {
        let mut v = Vec::new();
        let mut sum = LinExpr::new();
        for i in 0..=(k.iter().max().copied().unwrap()) {
            let var = model
                .add_var(
                    &format!("{:?}:{}", k, i),
                    Integer,
                    0.0,
                    -INFINITY,
                    INFINITY,
                    &[],
                    &[],
                )
                .unwrap();

            sum = sum + &var;
            v.push(var);
        }

        model
            .add_constr(&format!("{:?}", k), sum, Equal, c as f64)
            .unwrap();
        variables.push(v);
    }

    for i in 0..input.len() {
        let mut sum = LinExpr::new();

        for (l, (k, _)) in counts.iter().enumerate() {
            for j in 0..input.len() {
                if k[i] != k[j] {
                    sum = sum + &variables[l][k[j] as usize];
                }
            }
        }

        model
            .add_constr(&format!("{:?}", i), sum, Less, d as f64)
            .unwrap();
    }

    model.update().unwrap();

    model.optimize().unwrap();

    dbg!(model.status().unwrap());
}

fn main() {
    let args = Args::parse();

    let input = read_input(args.filename);

    solve(&input, 6);
}
