module history.data_impl_sqlite;

import std.datetime;
import handy_http_primitives : Optional;
import d2sqlite3;

import history.data;
import history.model;
import util.sqlite;

class SqliteHistoryRepository : HistoryRepository {
    private Database db;
    this(Database db) {
        this.db = db;
    }

    Optional!History findById(ulong id) {
        Statement stmt = db.prepare("SELECT * FROM history WHERE id = ?");
        stmt.bind(1, id);
        ResultRange result = stmt.execute();
        if (result.empty) return Optional!History.empty;
        return Optional!History.of(parseHistory(result.front));
    }

    HistoryItem[] findItemsBefore(ulong historyId, SysTime timestamp, uint limit) {
        import std.conv;
        import std.algorithm;
        import std.array;
        const query = q"SQL
            SELECT * FROM history_item
            WHERE history_id = ? AND timestamp <= ?
            ORDER BY timestamp DESC
            LIMIT
SQL";
        Statement stmt = db.prepare(query ~ " " ~ to!string(limit));
        stmt.bind(1, historyId);
        stmt.bind(2, timestamp.toISOExtString());
        ResultRange result = stmt.execute();
        return result.map!(r => parseItem(r)).array;
    }

    HistoryItemText getTextItem(ulong itemId) {
        Statement stmt = db.prepare("SELECT * FROM history_item_text WHERE item_id = ?");
        stmt.bind(1, itemId);
        ResultRange result = stmt.execute();
        if (result.empty) throw new Exception("No history item exists.");
        return parseTextItem(result.front);
    }

    void addTextItem(ulong historyId, SysTime timestamp, string text) {
        ulong itemId = addItem(historyId, timestamp, HistoryItemType.TEXT);
        update(
            db,
            "INSERT INTO history_item_text (item_id, content) VALUES (?, ?)",
            itemId, text
        );
    }

    void deleteById(ulong id) {
        Statement stmt = db.prepare("DELETE FROM history WHERE id = ?");
        stmt.bind(1, id);
        stmt.execute();
    }

    static History parseHistory(Row row) {
        return History(row.peek!ulong(0));
    }

    static HistoryItem parseItem(Row row) {
        return HistoryItem(
            row.peek!ulong(0),
            row.peek!ulong(1),
            parseISOTimestamp(row, 2),
            getHistoryItemType(row.peek!(string, PeekMode.slice)(3))
        );
    }

    static HistoryItemText parseTextItem(Row row) {
        return HistoryItemText(row.peek!ulong(0), row.peek!string(1));
    }

    private ulong addItem(ulong historyId, SysTime timestamp, HistoryItemType type) {
        update(
            db,
            "INSERT INTO history_item (history_id, timestamp, type) VALUES (?, ?, ?)",
            historyId, timestamp, type
        );
        return db.lastInsertRowid();
    }
}