use std::{
    collections::HashMap,
    fs::File,
    io::{Read, Write},
    sync::{Arc, OnceLock},
};

use eframe::egui::{self, mutex::RwLock, Color32, Label, RichText, Sense, Stroke, Vec2};
use serde::{Deserialize, Serialize};

use crate::{
    tabs::{
        activities::{activities, get_lec_short_name, get_teachers_short_name, Activity},
        classes::{get_classes, selected_class, set_selected_class},
        generating::Schedule,
    },
    DAYS,
};

use super::save_classes_limitations;

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ClassLimitation {
    pub class_id: i32,
    pub limitations: Vec<Vec<bool>>,
}
impl ClassLimitation {
    pub fn new(id: i32) -> Self {
        Self {
            class_id: id,
            limitations: vec![vec![true; 10]; 7],
        }
    }
}
pub fn classes_limitations() -> &'static Arc<RwLock<HashMap<i32, ClassLimitation>>> {
    static PAGE: OnceLock<Arc<RwLock<HashMap<i32, ClassLimitation>>>> = OnceLock::new();
    PAGE.get_or_init(|| Arc::new(RwLock::new(get_class_limitations())))
}
pub fn schedules() -> &'static Arc<RwLock<Vec<Schedule>>> {
    static PAGE: OnceLock<Arc<RwLock<Vec<Schedule>>>> = OnceLock::new();
    PAGE.get_or_init(|| Arc::new(RwLock::new(vec![])))
}

pub fn get_class_limitations() -> HashMap<i32, ClassLimitation> {
    let lims = File::open("./db/classes_limitations.json");
    if let Ok(mut lims) = lims {
        let mut content: String = String::new();
        lims.read_to_string(&mut content).unwrap();
        let cls: Vec<ClassLimitation> = serde_json::from_str(content.as_str()).unwrap();
        let mut lims: HashMap<i32, ClassLimitation> = HashMap::new();
        for c in cls {
            lims.insert(c.class_id, c);
        }
        return lims;
    }
    HashMap::new()
}
type ClassSched = Arc<RwLock<HashMap<i32, Vec<Vec<Option<Activity>>>>>>;
pub fn class_sched() -> &'static ClassSched {
    static ACT: OnceLock<ClassSched> = OnceLock::new();
    ACT.get_or_init(|| Arc::new(RwLock::new(HashMap::new())))
}
pub fn class_limitation() -> &'static Arc<RwLock<ClassLimitation>> {
    static LIM: OnceLock<Arc<RwLock<ClassLimitation>>> = OnceLock::new();
    LIM.get_or_init(|| Arc::new(RwLock::new(ClassLimitation::default())))
}

pub fn create_sched(id: i32) {
    // let id = selected_class().read().unwrap();
    let class_acts = activities()
        .read()
        .iter()
        .filter(|a| a.1.classes.iter().any(|c| c == &id))
        .map(|a| a.1.clone())
        .collect::<Vec<Activity>>();
    // let mut scheds = vec![];
    let scs = (*schedules().read()).clone();
    let sched = scs
        .iter()
        .filter(|s| class_acts.iter().any(|a| a.id == s.activity))
        .map(|s| s.clone())
        .collect::<Vec<Schedule>>();
    let mut scheduls: Vec<Vec<Option<Activity>>> = vec![vec![None; 10]; 7];
    sched.iter().for_each(|s| {
        scheduls[s.day_id as usize - 1][s.hour as usize] = class_acts
            .clone()
            .into_iter()
            .find(|ca| ca.id == s.activity);
    });
    std::thread::spawn(move || {
        let c_sched = Arc::clone(class_sched());
        c_sched.write().insert(id, scheduls);
    });
}
pub fn save_class_limitations() {
    let lims = &*classes_limitations().read();
    let lims = lims.values().collect::<Vec<&ClassLimitation>>();
    let mut f = File::create("./db/classes_limitations.json").unwrap();
    f.write_all(serde_json::to_string(&lims).unwrap().as_bytes())
        .expect("Kısıtlamalar Yazılamadı");
}

pub fn update_class_limitation() {
    let class_lims = &*class_limitation().read();
    let mut lims = classes_limitations().write();
    let id = selected_class().read().unwrap();
    lims.insert(id, class_lims.clone());
}
pub fn class_lim_ui(ui: &mut egui::Ui) {
    for c in &*get_classes().read() {
        create_sched(c.1.id);
    }
    egui::SidePanel::left("right_panel")
        .resizable(true)
        .default_width(250.0)
        .width_range(150.0..=300.0)
        .show_inside(ui, |ui| {
            ui.vertical_centered(|ui| {
                let class_id = selected_class().read();
                let mut classes = get_classes().write();
                for c in &mut *classes {
                    let fill_color = if class_id.is_some_and(|c2| c2 == c.1.id) {
                        Color32::LIGHT_BLUE
                    } else {
                        Color32::default()
                    };
                    let stroke_color = if class_id.is_some_and(|c2| c2 == c.1.id) {
                        Color32::DARK_BLUE
                    } else {
                        Color32::default()
                    };
                    let button = egui::Frame::none()
                        .fill(fill_color)
                        .stroke(Stroke::new(1., stroke_color))
                        .show(ui, |ui| {
                            ui.label(&c.1.name);
                        })
                        .response;
                    let button = button
                        .interact(egui::Sense::click())
                        .on_hover_cursor(egui::CursorIcon::PointingHand);
                    if button.clicked() {
                        set_selected_class(c.1.id);
                    }
                }
            });
        });
    egui::CentralPanel::default()
        // .resizable(true)
        // .default_width(1024.0)
        // .width_range(800.0..=1280.0)
        .show_inside(ui, |ui| {
            class_limitation_ui(ui);
        });
}

fn class_limitation_ui(ui: &mut egui::Ui) {
    if let None = *selected_class().read() {
        return ();
    }
    // ui.set_width(1024.)
    let class_id = selected_class().read().unwrap();
    let c_sched = class_sched().read();
    let c_sched = c_sched.get(&class_id);
    if let Some(c_sched) = c_sched {
        ui.add_space(50.);
        ui.horizontal(|ui| {
            ui.add_space(100.);
            egui::Grid::new("some_unique_id")
                .min_col_width(100.)
                .max_col_width(100.)
                .min_row_height(100.)
                // .with_row_color(100.)
                .spacing((1.0, 1.0))
                .show(ui, |ui| {
                    hours_row(ui);
                    ui.end_row();
                    days_row(ui, c_sched);
                });
            ui.end_row();
        });
        ui.vertical_centered(|ui| {
            ui.add_space(20.);
            if ui.button("Kaydet").clicked() {
                update_class_limitation();
            };
            if ui.button("Tümünü dosyaya Kaydet").clicked() {
                save_classes_limitations();
            };
        });
    }
}
fn days_row(ui: &mut egui::Ui, c_sched: &Vec<Vec<Option<Activity>>>) {
    for day in DAYS.iter().enumerate() {
        ui.painter().rect(
            ui.available_rect_before_wrap(),
            0.0,
            Color32::default(),
            Stroke::new(1.0, Color32::LIGHT_GRAY),
        );
        if ui
            .add_sized(Vec2::new(100., 50.), Label::new(*day.1))
            .interact(Sense::click())
            .clicked()
        {
            change_all_day(day.0);
        };
        for i in 1..=10 {
            row_paint(ui, day.0, i - 1);
            let r = ui.add_sized(
                Vec2::new(100., 50.),
                Label::new(sched_act(&c_sched[day.0][i - 1])),
            );
            let r = r
                .interact(Sense::click())
                .on_hover_cursor(egui::CursorIcon::PointingHand);
            if r.clicked() {
                change_row(day.0, i - 1);
            }
        }
        ui.end_row();
    }
}
fn hours_row(ui: &mut egui::Ui) {
    ui.painter().rect(
        ui.available_rect_before_wrap(),
        0.0,
        Color32::default(),
        Stroke::new(1.0, Color32::LIGHT_GRAY),
    );
    ui.label("Günler ve Saatler");
    for i in 1..=10 {
        ui.painter().rect(
            ui.available_rect_before_wrap(),
            0.0,
            Color32::default(),
            Stroke::new(1.0, Color32::LIGHT_GRAY),
        );
        if ui
            .add_sized(
                Vec2::new(100., 50.),
                Label::new(RichText::new(format!("{i}. Saat")).color(Color32::WHITE)),
            )
            .interact(Sense::click())
            .clicked()
        {
            change_all_hours(i - 1);
        };
    }
}

fn row_paint(ui: &mut egui::Ui, day: usize, hour: usize) {
    let lims = &*class_limitation().read().limitations;
    if lims[day][hour] {
        ui.painter()
            .rect_filled(ui.available_rect_before_wrap(), 0.0, Color32::BROWN);
    }
    ui.painter().rect(
        ui.available_rect_before_wrap(),
        0.0,
        Color32::default(),
        Stroke::new(1., Color32::WHITE),
    );
}

fn change_row(day: usize, hour: usize) {
    let lims = &*class_limitation();
    std::thread::spawn(move || {
        let lim = Arc::clone(lims);
        let row = lim.read().limitations[day][hour];
        lim.write().limitations[day][hour] = !row;
    });
}
fn sched_act(a: &Option<Activity>) -> String {
    let Some(a) = a else {
        return "".to_string();
    };
    let mut activity = a.id.to_string();
    activity.push_str("-");
    activity.push_str(&get_lec_short_name(a.lecture));
    activity.push_str("\n");
    // activity.push_str(&get_classes_name(&a.classes));
    activity.push_str(&get_teachers_short_name(&a.teachers));
    activity
}
fn change_all_day(day: usize) {
    let lims = &*class_limitation();
    std::thread::spawn(move || {
        let lim = Arc::clone(lims);
        let day = &mut lim.write().limitations[day];
        if day.iter().any(|h| !*h) {
            *day = vec![true; 10];
        } else {
            *day = vec![false; 10];
        }
        // lim.write().limitations[day][hour] = !row;
    });
}
fn change_all_hours(hour: usize) {
    let lims = &*class_limitation();
    std::thread::spawn(move || {
        let lim = Arc::clone(lims);
        let day = &mut lim.write().limitations;
        if day.iter().any(|h| !h[hour]) {
            day.iter_mut().for_each(|d| d[hour] = true);
        } else {
            day.iter_mut().for_each(|d| d[hour] = false);
            // *day = vec![false; 10];
        }
        // lim.write().limitations[day][hour] = !row;
    });
}