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

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

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

    let category = event.category.map(|id| id.hyphenated().to_string());

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

    Ok(())
}

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

    let category = event.category.map(|id| id.hyphenated().to_string());

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

    Ok(())
}

pub fn delete(conn: &Connection, id: &Uuid) -> Result<()> {
    conn.execute(
        "DELETE FROM events WHERE id = ?",
        params![id.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, category
        FROM events
        WHERE repetition IS NOT NULL",
    )?;

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

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

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, category
        FROM events
        WHERE 
            repetition IS NULL 
            AND date >= ?
            AND date <= ?
    ",
    )?;

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

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

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

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