(local sqlite (require :lsqlite3))
(local lume (require :lume))
(local { : view} (require :fennel))

(fn table-exists? [db]
  (accumulate [ret false
               r (db:nrows "select name from sqlite_master where type = 'table' and name= 'db_version'")]
    (or ret true)))

(fn migrate [db serial statement]
  (if (= (db:exec statement) 0)
      (db:exec (.. "update db_version set serial = " serial))
      (assert false (.. "db failed: " statement))))

(fn migrate-all [db]
  (when (not (table-exists? db "db_version"))
    (db:exec "create table db_version (serial integer)")
    (db:exec "insert into db_version values (1)"))
  (let [version (accumulate [v 0
                             r (db:nrows "select serial from db_version")]
                  r.serial)]
    (when (< version 2)
      (migrate db 2 "create table visits (url text, timestamp integer)"))
    (when (< version 3)
      (migrate db 3 "create table page_titles (url text primary key, title text)"))))

(fn visit [self url timestamp]
  (let [s (self.db:prepare "insert into visits (url, timestamp) values (?,?)")]
    (assert (= 0 (s:bind_values url timestamp)) (view [url timestamp]))
    (s:step)
    (s:reset)))

(fn title [self url title]
  (let [s (self.db:prepare "insert into page_titles (url, title) values (?,?) on conflict(url) do update set title = excluded.title")]
    (assert (= 0 (s:bind_values url title)) url)
    (s:step)
    (s:reset)))

(fn find [self term]
  (let [s (self.db:prepare "select v.url,pt.title,v.timestamp from visits v left join page_titles pt on v.url = pt.url where instr(v.url, ?) is not null")]
    (assert (= 0 (s:bind_values term)))
    (icollect [r (s:nrows)] r)))

(fn open [pathname]
  (let [db (if pathname (sqlite:open pathname) (sqlite:open_memory))]
    (migrate-all db)
    {
     : db
     : visit
     : title
     : find
     }))

{
 : open
 }
