use anyhow::Result;
use chrono::{NaiveDate, NaiveTime};
use rusqlite::{params, Connection};
use rusqlite_migration::{Migrations, M};
use uuid::Uuid;

use crate::model::event::Event;

pub fn init(db_path: &str) -> Result<Connection> {
    let mut conn = Connection::open(db_path)?;
    let migrations = Migrations::new(vec![M::up(include_str!("migrations/1-init.sql"))]);
    migrations.to_latest(&mut conn)?;
    Ok(conn)
}

pub fn insert(conn: &Connection, event: &Event) -> Result<()> {
    let repetition = match &event.repetition {
        Some(r) => Some(serde_json::to_string(&r)?),
        None => None,
    };

    conn.execute(
        "INSERT INTO events (id, date, start, end, name, repetition, created, updated) VALUES (?, ?, ?, ?, ?, ?, datetime(), datetime())",
        params![event.id.to_hyphenated().to_string(), event.date, event.start, event.end, event.name, repetition]
    )?;

    Ok(())
}

pub fn update(conn: &Connection, event: &Event) -> Result<()> {
    let repetition = match &event.repetition {
        Some(r) => Some(serde_json::to_string(&r)?),
        None => None,
    };

    conn.execute(
        "UPDATE events SET date = ?, start = ?, end = ?, name = ?, repetition = ?, updated = datetime() WHERE id = ?",
        params![event.date, event.start, event.end, event.name, repetition, event.id.to_hyphenated().to_string()]
    )?;

    Ok(())
}

pub fn delete(conn: &Connection, id: &Uuid) -> Result<()> {
    conn.execute(
        "DELETE FROM events WHERE id = ?",
        params![id.to_hyphenated().to_string()],
    )?;

    Ok(())
}

pub fn list_recurring(conn: &Connection) -> Result<Vec<Event>> {
    let mut stmt = conn.prepare(
        "
        SELECT id, date, start, end, name, repetition
        FROM events
        WHERE repetition IS NOT NULL",
    )?;

    let iter = stmt.query_map([], |row| {
        Ok(read_recurring_event(
            row.get(0)?,
            row.get(1)?,
            row.get(2)?,
            row.get(3)?,
            row.get(4)?,
            row.get(5)?,
        ))
    })?;

    let mut res = vec![];
    for event in iter {
        res.push(event??)
    }
    Ok(res)
}

fn read_recurring_event(
    uuid: String,
    date: NaiveDate,
    start: Option<NaiveTime>,
    end: Option<NaiveTime>,
    name: String,
    repetition: Option<String>,
) -> Result<Event> {
    let id = Uuid::parse_str(&uuid)?;
    let repetition = match repetition {
        Some(r) => Some(serde_json::from_str(&r)?),
        None => None,
    };

    Ok(Event {
        id,
        date,
        start,
        end,
        name,
        repetition,
    })
}

pub fn list_non_recurring_between(
    conn: &Connection,
    start: NaiveDate,
    end: NaiveDate,
) -> Result<Vec<Event>> {
    let mut stmt = conn.prepare(
        "
        SELECT id, date, start, end, name 
        FROM events
        WHERE 
            repetition IS NULL 
            AND date >= ?
            AND date <= ?
    ",
    )?;

    let iter = stmt.query_map([start, end], |row| {
        let uuid: String = row.get(0)?;
        let date = row.get(1)?;
        let start = row.get(2)?;
        let end = row.get(3)?;
        let name = row.get(4)?;
        Ok(Uuid::parse_str(&uuid).map(|id| Event {
            id,
            date,
            start,
            end,
            name,
            repetition: None,
        }))
    })?;

    let mut res = vec![];
    for event in iter {
        res.push(event??)
    }
    Ok(res)
}