use rand::Rng;
use serde::{Deserialize, Serialize};
use symbios_genetics::{Evaluator, Evolver, Genotype, algorithms::simple::SimpleGA};

const TARGET: &str = "Sovereign Symbiosis";

#[derive(Clone, Serialize, Deserialize, Debug)]
struct StringDNA(Vec<u8>);

impl Genotype for StringDNA {
    fn mutate<R: Rng>(&mut self, rng: &mut R, rate: f32) {
        for byte in &mut self.0 {
            if rng.random::<f32>() < rate {
                *byte = rng.random_range(32..126);
            }
        }
    }

    fn crossover<R: Rng>(&self, other: &Self, rng: &mut R) -> Self {
        let split = rng.random_range(0..self.0.len());
        let mut child = self.0[..split].to_vec();
        child.extend_from_slice(&other.0[split..]);
        StringDNA(child)
    }
}

struct StringEvaluator;
impl Evaluator<StringDNA> for StringEvaluator {
    fn evaluate(&self, genotype: &StringDNA) -> (f32, Vec<f32>, Vec<f32>) {
        let fitness = genotype
            .0
            .iter()
            .zip(TARGET.as_bytes())
            .filter(|(a, b)| a == b)
            .count() as f32;
        (fitness, vec![fitness], vec![])
    }
}

fn main() {
    let mut rng = rand::rng();
    let initial_pop: Vec<StringDNA> = (0..100)
        .map(|_| {
            StringDNA(
                (0..TARGET.len())
                    .map(|_| rng.random_range(32..126))
                    .collect(),
            )
        })
        .collect();

    let mut ga = SimpleGA::new(initial_pop, 0.05, 5, 42);
    let eval = StringEvaluator;

    for generation in 0..500 {
        ga.step(&eval);
        let best = &ga.population()[0];
        let current_str = String::from_utf8_lossy(&best.genotype.0);

        if generation % 50 == 0 {
            println!(
                "Gen {}: [{}] (Fitness: {})",
                generation, current_str, best.fitness
            );
        }

        if current_str == TARGET {
            println!("🎯 Target reached at Gen {}!", generation);
            break;
        }
    }
}
