use std::{collections::HashMap, io::{Read as _, Write as _}};

use utils::serialize::{DeserializationError, Serialize, SetOfSerializedObjects};

use crate::{DEFAULT_CHANNEL, DEFAULT_PASSWORD, DEFAULT_USERNAME_PREFIX, SAVE_FILE, SAVE_FILE_FUZZY, message::Message, ui};


#[derive(Clone)]
pub struct Appdata {
    pub persistent_data: Savedata,

    pub messages: HashMap<String, Message>,
    pub quick_close_window_count: usize,
    pub local_channels: Vec<String>,
    pub intro_screen_shown_until: u128,
}

impl Default for Appdata {
    fn default() -> Self {
        Self::new()
    }
}

impl Appdata {
    pub fn new() -> Self {
        Self {
            messages: HashMap::new(),
            quick_close_window_count: 0,
            local_channels: vec![DEFAULT_CHANNEL.to_string()],
            intro_screen_shown_until: 0,
            persistent_data: Savedata::new(),
        }
    }
}

#[derive(Clone)]
pub struct Savedata {
    pub username: String,
    pub language: ui::Language,
    pub blocked_phrases: Vec<String>,
    pub blocked_phrases_censor_char: char,
    pub api_endpoint: String,
    pub api_refresh_rate: u64,
    pub current_channel: String,
    pub api_password: String,
}

impl Savedata {
    pub fn new() -> Self {
        Self {
            username: format!(
                "{}#{}",
                DEFAULT_USERNAME_PREFIX,
                utils::rng::random_numeric_string(4)
            ),
            language: ui::Language::English,
            api_endpoint: String::new(),
            api_refresh_rate: 10,
            blocked_phrases: Vec::new(),
            blocked_phrases_censor_char: '*',
            current_channel: DEFAULT_CHANNEL.to_string(),
            api_password: DEFAULT_PASSWORD.to_string(),
        }
    }
}

impl From<Appdata> for Savedata {
    fn from(value: Appdata) -> Self {
        value.persistent_data
    }
}

impl From<Savedata> for Appdata {
    fn from(value: Savedata) -> Self {
        let mut s = Self::new();

        s.persistent_data = value;

        s
    }
}

impl Serialize for ui::Language {
    const SIGNATURE: &'static str = "Language";
    const LENGTH: usize = 1;

    fn serialize_unchecked(&self) -> Vec<u8> {
        let b = match self {
            ui::Language::English => 1,
            ui::Language::Dutch => 2,
            ui::Language::Japanese => 3,
        };

        vec![b]
    }

    fn deserialize_unchecked<B>(data: B) -> utils::serialize::Result<Self>
    where
        B: AsRef<[u8]>,
        Self: Sized,
    {
        let data = data.as_ref();

        let byte = data
            .first()
            .ok_or(DeserializationError::InvalidDataLength)?;

        let language = match byte {
            1 => ui::Language::English,
            2 => ui::Language::Dutch,
            3 => ui::Language::Japanese,
            _ => return Err(DeserializationError::InvalidData("Invalid language byte")),
        };

        Ok(language)
    }
}

impl Serialize for Savedata {
    const SIGNATURE: &'static str = "Savedata";

    fn serialize_unchecked(&self) -> Vec<u8> {
        let mut data = Vec::new();

        data.push(self.username.serialize_checked());
        data.push(self.language.serialize_checked());
        data.push(self.blocked_phrases.serialize_checked());
        data.push(self.blocked_phrases_censor_char.serialize_checked());
        data.push(self.api_endpoint.serialize_checked());
        data.push(self.api_refresh_rate.serialize_checked());
        data.push(self.api_password.serialize_checked());
        data.push(self.current_channel.serialize_checked());

        data.serialize_checked()
    }

    fn deserialize_unchecked<B>(data: B) -> utils::serialize::Result<Self>
    where
        B: AsRef<[u8]>,
        Self: Sized,
    {
        let data = data.as_ref();
        let serialized_items = SetOfSerializedObjects::deserialize_checked(data)?;

        if serialized_items.len() != 8 {
            return Err(DeserializationError::InvalidDataLength);
        }

        let username = String::deserialize_checked(&serialized_items[0])?;
        let language = ui::Language::deserialize_checked(&serialized_items[1])?;
        let blocked_phrases = Vec::<String>::deserialize_checked(&serialized_items[2])?;
        let blocked_phrases_censor_char = char::deserialize_checked(&serialized_items[3])?;
        let api_endpoint = String::deserialize_checked(&serialized_items[4])?;
        let api_refresh_rate = u64::deserialize_checked(&serialized_items[5])?;
        let api_password = String::deserialize_checked(&serialized_items[6])?;
        let current_channel = String::deserialize_checked(&serialized_items[7])?;

        Ok(Self {
            username,
            language,
            blocked_phrases,
            blocked_phrases_censor_char,
            api_endpoint,
            api_refresh_rate,
            api_password,
            current_channel,
        })
    }
}

pub fn save_appdata(appdata: Appdata) {
    let savedata = Savedata::from(appdata);
    let bytes = savedata.serialize_checked();
    let fuzzy_bytes = utils::binary::fuzzy_bytes(bytes, SAVE_FILE_FUZZY);

    let mut file = std::fs::File::create(SAVE_FILE).expect("Failed to create savedata file.");
    file.write_all(&fuzzy_bytes)
        .expect("Failed to write savedata file.");

    logging::trace("Wrote savadata to file.");
}

pub fn load_appdata() -> std::io::Result<Option<Appdata>> {
    let mut df = logging::warn_deferred("Savedata file not found; using defaults");

    let mut file = std::fs::File::open(SAVE_FILE)?;

    let mut bytes = Vec::new();
    file.read_to_end(&mut bytes)?;
    let useful_bytes = utils::binary::fuzzy_bytes(bytes, SAVE_FILE_FUZZY);

    df.cancel();

    let Ok(savedata) = Savedata::deserialize_checked(useful_bytes) else {
        // If the file is corrupted, create a new one
        logging::warn("Savedata file corrupted; using defaults.");
        return Ok(None);
    };

    let appdata = Appdata::from(savedata);

    logging::trace("Savedata file loaded.");
    Ok(Some(appdata))
}
