use chrono::Local;
use clap::Parser;
use home::home_dir;
use rusqlite::{Connection, Result};

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
    /// Create a new note: --create "Hello World"
    #[arg(short, long, default_value_t = String::new())]
    create: String,
    /// Show the latest entries
    #[arg(short, long, default_value_t = false)]
    show: bool,
    /// Show all entries
    #[arg(long, default_value_t = false)]
    showall: bool,
    /// Append or show entries with tag
    #[arg(short, long, default_value_t = String::new())]
    tag: String,
}

struct Note {
    content: String,
    date: String,
    tag: String,
}

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

    match init_db() {
        Ok(_) => println!(),
        Err(e) => println!("Failed to initialize DB: {}", e),
    };

    if args.show {
        match show_notes(false, &args.tag) {
            Ok(_) => println!(),
            Err(e) => println!("Failed to show DB: {}", e),
        }
    }

    if args.create != String::new() {
        match create_note(&args.create, &args.tag) {
            Ok(_) => println!(),
            Err(e) => println!("Failed to throw into DB: {}", e),
        }
    }

    if args.showall {
        match show_notes(true, &args.tag) {
            Ok(_) => println!(),
            Err(e) => println!("Failed to show DB: {}", e),
        }
    }

    if args.tag != String::new() && args.create == String::new() {
        match show_notes(true, &args.tag) {
            Ok(_) => println!(),
            Err(e) => println!("Failed to show DB: {}", e),
        }
    }
}

fn init_db() -> Result<()> {
    let home = home_dir().unwrap().join(".snotes.db");
    let connection = Connection::open(home)?;

    let query_table = "
        CREATE TABLE IF NOT EXISTS notes (
            nid INTEGER PRIMARY KEY AUTOINCREMENT,
            content TEXT NOT NULL,
            date TEXT NOT NULL,
            tag TEXT
        );
    ";

    match connection.execute(query_table, []) {
        Ok(v) => println!("INIT OK {}", v),
        Err(e) => println!("INIT ERR {}", e),
    };

    Ok(())
}

fn create_note(content: &String, tag: &String) -> Result<()> {
    let home = home_dir().unwrap().join(".snotes.db");
    let connection = Connection::open(home)?;
    let date = Local::now();
    let date_string = date.format("%m-%d-%y %H:%M").to_string();
    let tag_string = if tag.to_string() == String::new() {
        String::from("quick")
    } else {
        tag.to_string()
    };

    let query_insert = "INSERT INTO notes (content, date, tag) VALUES (?1, ?2, ?3)";

    match connection.execute(query_insert, [&content, &date_string, &tag_string]) {
        Ok(v) => println!("CREATE OK {}", v),
        Err(e) => println!("CREATE ERR {}", e),
    };

    Ok(())
}

fn show_notes(all: bool, tag: &String) -> Result<()> {
    let home = home_dir().unwrap().join(".snotes.db");
    let connection = Connection::open(home)?;

    let mut query = String::from("SELECT * FROM notes LIMIT 10");

    if all {
        query = String::from("SELECT * FROM notes");
    }

    if tag != &String::new() {
        query = format!("SELECT * FROM notes WHERE tag IS '{}'", tag);
    }

    let mut prepare = connection.prepare(&query)?;

    let notes = prepare.query_map([], |row| {
        Ok(Note {
            content: row.get(1)?,
            date: row.get(2)?,
            tag: row.get(3)?,
        })
    })?;

    for note in notes {
        let unwrapped = note.unwrap();
        println!(
            "{0} #{2}: {1}",
            &unwrapped.date, &unwrapped.content, &unwrapped.tag
        );
    }
    Ok(())
}