login

local lud = require "ludweb"

local schema = [[
CREATE TABLE IF NOT EXISTS User (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    nick TEXT NOT NULL UNIQUE,
    name TEXT NOT NULL,
    salt TEXT,
    hash TEXT
);
CREATE TABLE IF NOT EXISTS Project (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL UNIQUE,
    desc TEXT
);
CREATE TABLE IF NOT EXISTS Membership (
    proj_id INTEGER REFERENCES Project(id),
    user_id INTEGER REFERENCES User(id),
    PRIMARY KEY(proj_id, user_id)
);
CREATE TABLE IF NOT EXISTS State (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS Ticket (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    proj_id INTEGER NOT NULL REFERENCES Project(id),
    user_id INTEGER NOT NULL REFERENCES User(id),
    state_id INTEGER NOT NULL REFERENCES State(id),
    time INTEGER NOT NULL,
    code INTEGER NOT NULL,
    title TEXT NOT NULL,
    desc TEXT
);
CREATE TABLE IF NOT EXISTS Shift (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    ticket_id INTEGER NOT NULL REFERENCES Ticket(id),
    old_stt_id INTEGER NOT NULL REFERENCES State(id),
    new_stt_id INTEGER NOT NULL REFERENCES State(id),
    user_id INTEGER NOT NULL REFERENCES User(id),
    time INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS Comment (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    ticket_id INTEGER NOT NULL REFERENCES Ticket(id),
    user_id INTEGER NOT NULL REFERENCES User(id),
    time INTEGER NOT NULL,
    text TEXT NOT NULL
);
]]

local Model = {}
Model.__index = Model

function Model:create_tables()
    self.db:execute_many(schema)
end

function Model:create_states()
    self.db:execute[[
        INSERT INTO State(name) VALUES
        ("backlog"), ("design"), ("progress"), ("review"), ("done");
    ]]
end

function Model:get_user(nick)
    return self.db:execute("SELECT * FROM User WHERE nick = ?;", nick)[1]
end

function Model:get_user_projects(user_id)
    return self.db:execute([[
        SELECT * FROM Project JOIN Membership ON Project.id = Membership.proj_id
        WHERE Membership.user_id = ?;
    ]], user_id)
end

function Model:get_states()
    return self.db:execute("SELECT * FROM State;")
end

function Model:get_tickets(proj_id, state_id)
    -- TODO: limit "done" tickets by age
    local query = "SELECT * FROM Ticket WHERE proj_id = ? AND state_id = ?;"
    return self.db:execute(query, proj_id, state_id)
end

function Model:get_comments(ticket_id)
    return self.db:execute("SELECT * FROM Comment WHERE ticket_id = ?;", ticket_id)
end

function Model:create_user(nick, name, salt, hash)
    local query = "INSERT INTO User(nick, name, salt, hash) VALUES (?, ?, ?, ?);"
    self.db:execute(query, nick, name, salt, hash)
end

function Model:create_project(user_id, name, desc)
    self.db:execute("INSERT INTO Project(name, desc) VALUES (?, ?);", name, desc)
    local proj_id = self.db:execute("SELECT id FROM Project WHERE name = ?;", name)[1].id
    self.db:execute("INSERT INTO Membership(proj_id, user_id) VALUES (?, ?);", proj_id, user_id)
    return proj_id
end

-- generate new ticket code for a given project
function Model:get_next_code(proj_id)
    local query = "SELECT code FROM Ticket WHERE proj_id = ? ORDER BY code DESC LIMIT 1;"
    local last_ticket = self.db:execute(query, proj_id)[1]
    local last_code
    if last_ticket == nil then
        last_code = 0
    else
        last_code = last_ticket.code
    end
    return last_code + 1
end

function Model:create_ticket(user_id, proj_id, title, desc)
    local code = self:get_next_code(proj_id)
    self.db:execute([[
        INSERT INTO Ticket(proj_id, user_id, state_id, time, code, title, desc)
        VALUES (?, ?, 1, unixepoch(), ?, ?, ?);
    ]], proj_id, user_id, code, title, desc)
    return code
end

function Model:shift_ticket(user_id, ticket, state_id)
    self.db:execute([[
        INSERT INTO Shift(ticket_id, old_stt_id, new_stt_id, user_id, time)
        VALUES (?, ?, ?, ?, unixepoch());
    ]], ticket.id, ticket.state_id, state_id, user_id)
    self.db:execute("UPDATE Ticket SET state_id = ? WHERE id = ?;", state_id, ticket.id)
end

function Model:add_comment(user_id, ticket_id, text)
    self.db:execute[[
        INSERT INTO Comment(ticket_id, user_id, time, text)
        VALUES (?, ?, unixepoch(), ?);
    ]], ticket_id, user_id, text)
end

function Model:close()
    self.db:close()
end

local function open(path)
    local self = setmetatable({path=path}, Model)
    self.db = lud.sqlite.open(path)
    return self
end

return {open=open}