use rusqlite::Connection;
use core::panic;
use std::fs::{self, File};
use std::io::{ErrorKind, Write};

use crate::db::{get_db_path, get_program_home_path};

#[derive(PartialEq)]
pub struct DictionaryEntry {
    hieroglyph: String,
    reading: String,
    translation: String,
}

impl std::clone::Clone for DictionaryEntry {
    fn clone(&self) -> Self {
        Self {
            hieroglyph: self.hieroglyph.clone(),
            reading: self.reading.clone(),
            translation: self.translation.clone(),
        }
    }
}

impl std::fmt::Display for DictionaryEntry {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(
            f,
            "{}:{}:{}",
            self.hieroglyph, self.reading, self.translation
        )
    }
}

fn get_sym_pos_or_panic(path: &String, line: &str, sym: &str, line_number: usize) -> usize {
    match line.find(sym) {
        Some(c) => c,
        None => panic!(
            "Failed to parse {} on line {}. Cannot find '{}'.",
            path, line_number, sym
        ),
    }
}
#[tokio::main]
async fn download_dictionary(path: String) {
    let link = "https://git.foxarmy.org/leca/learn-hieroglyphs/raw/branch/master/dictionary.txt";
    let response = reqwest::get(link).await;

    let mut file = match File::create(&path) {
        Err(e) => panic!("Could not create dictionary file. {}", e),
        Ok(file) => file
    };
    let content = response.unwrap().bytes().await.unwrap();
    file.write_all(&content).unwrap();
    import(&path);
}

fn read_and_parse(path: &String) -> Vec<DictionaryEntry> {
    let mut result: Vec<DictionaryEntry> = Vec::new();
    let content = match fs::read_to_string(path) {
        Ok(string) => string,
        Err(kind) => match kind.kind() {
            ErrorKind::NotFound => {
                println!("No dictionary file found. Downloading it to {}/dictionary.txt from https://git.foxarmy.org/leca/learn-hieroglyphs", get_program_home_path());
                download_dictionary(path.to_owned());
                return Vec::new();
            }
            ErrorKind::PermissionDenied => {
                println!("Permission denied while was trying to access the dictionary.txt file. Assuming it's empty");
                return Vec::new();
            }
            _ => {
                println!("Unexpected error. Can't load dictionary. Assuming it's empty");
                return Vec::new();
            },
        },
    };

    let content: Vec<&str> = content.split('\n').collect();
    for (line_number, line) in content.iter().enumerate() {
        if *line == "" {
            continue;
        }
        let hieroglyph_start = get_sym_pos_or_panic(path, line, "(", line_number) + 1;
        let hieroglyph_end = get_sym_pos_or_panic(path, line, ")", line_number);

        let reading_start = get_sym_pos_or_panic(path, line, "[", line_number) + 1;
        let reading_end = get_sym_pos_or_panic(path, line, "]", line_number);

        let translation_start = get_sym_pos_or_panic(path, line, "{", line_number) + 1;
        let translation_end = get_sym_pos_or_panic(path, line, "}", line_number);

        let hieroglyph = line[hieroglyph_start..hieroglyph_end].to_string();
        let reading = if reading_start == reading_end {
            String::from("")
        } else {
            line[reading_start..reading_end].to_string()
        };
        let translation = line[translation_start..translation_end].to_string();

        let entry = DictionaryEntry {
            hieroglyph,
            reading,
            translation,
        };

        result.push(entry);
    }
    result
}

fn deduplicate(entries: &Vec<DictionaryEntry>) -> Vec<DictionaryEntry> {
    let mut deduplicated: Vec<DictionaryEntry> = Vec::new();

    for entry in entries.iter() {
        let mut is_dup = false;

        for e in deduplicated.iter() {
            if e.hieroglyph == entry.hieroglyph {
                is_dup = true;
                break;
            }
        }

        if is_dup {
            let pos = deduplicated
                .iter()
                .position(|x| x.hieroglyph == entry.hieroglyph)
                .unwrap();
            deduplicated[pos].translation += &(String::from(";") + &entry.translation);
        } else {
            deduplicated.push(entry.clone());
        }
    }

    deduplicated
}

fn write_to_database(dict: &Vec<DictionaryEntry>) {
    let mut conn = Connection::open(get_db_path()).unwrap();
    let tx = conn.transaction().unwrap();
    for line in dict.iter() {
        match tx.execute("INSERT OR IGNORE INTO cards (id, hieroglyph, reading, translation) VALUES ((SELECT id FROM cards WHERE hieroglyph = ?1), ?1, ?2, ?3)", [&line.hieroglyph, &line.reading, &line.translation]) {
            Ok(_) => (),
            Err(e) => {
                println!("{e}"  );
                panic!("Cannot update database with words form dictionary")
            }
        };
    }
    tx.commit().unwrap();
}

pub fn import(path: &String) {
    let dictionary = read_and_parse(path);
    let dictionary = deduplicate(&dictionary);
    write_to_database(&dictionary);
}

// pub fn write_to_file(path: &String, dict: &Vec<Entry>) {
//     let mut output = OpenOptions::new()
//     .write(true)
//     .append(true)
//     .open("./reparsed-test.txt")
//     .unwrap();

//     for entry in dict {
//         if let Err(e) = writeln!(
//             output,
//             "({}) [{}] {{{}}}",
//             entry.hieroglyph, entry.reading, entry.translation
//         ) {
//             eprintln!("Couldn't write to file: {}", e);
//         }
//     }
// }
