package main

import (
	"database/sql"
	_ "github.com/mattn/go-sqlite3"
	"todo/models"
)

type DatabaseInterface interface {
	GetTodos() ([]models.Todo, error)
	GetTodo(int) (models.Todo, error)
	CreateTodo(models.Todo) error
	DoTodo(int) error
	UndoTodo(int) error
	DeleteTodo(int) error
}

type DatabaseStore struct {
	DB *sql.DB
}

func MustNewDB() *DatabaseStore {
	db, err := sql.Open("sqlite3", "./database.sqlite")
	if err != nil {
		panic(err)
	}

	err = db.Ping()
	if err != nil {
		panic(err)
	}

	return &DatabaseStore{db}
}

func (store *DatabaseStore) GetTodos() ([]models.Todo, error) {
	selectAll := `select id, todo, done from todo`

	rows, err := store.DB.Query(selectAll)
	if err != nil {
		return []models.Todo{}, err
	}
	defer rows.Close()

	todos := []models.Todo{}
	for rows.Next() {
		todo := models.Todo{}
		err = rows.Scan(&todo.Id, &todo.Todo, &todo.Done)
		if err != nil {
			return []models.Todo{}, err
		}
		todos = append(todos, todo)
	}

	return todos, nil
}

func (store *DatabaseStore) GetTodo(id int) (models.Todo, error) {
	selectOne := `select id, todo, done from todo where id = ?`

	var todo models.Todo
	err := store.DB.QueryRow(selectOne, id).Scan(&todo.Id, &todo.Todo, &todo.Done)
	if err != nil {
		return models.Todo{}, err
	}

	return todo, nil
}

func (store *DatabaseStore) CreateTodo(todo models.Todo) error {
	createTodo := `insert into todo (todo, done) values (?, ?)`

	statement, err := store.DB.Prepare(createTodo)
	if err != nil {
		return err
	}
	defer statement.Close()

	_, err = statement.Exec(todo.Todo, false)
	if err != nil {
		return err
	}

	return nil
}

func (store *DatabaseStore) ToggleTodo(id int) error {
    todo, err := store.GetTodo(id)
	if err != nil {
		return err
	}

	setDone := `update todo set done = ? where id = ?`

	statement, err := store.DB.Prepare(setDone)
	if err != nil {
		return err
	}
	defer statement.Close()

	_, err = statement.Exec(!todo.Done, id)
	if err != nil {
		return err
	}

	return nil
}

func (store *DatabaseStore) UpdateTodo(todo models.Todo) error {
	updateTodo := `update todo set todo = ? where id = ?`

	statement, err := store.DB.Prepare(updateTodo)
	if err != nil {
		return err
	}
	defer statement.Close()

	_, err = statement.Exec(todo.Todo, todo.Id)
	if err != nil {
		return err
	}

	return nil
}

func (store *DatabaseStore) DeleteTodo(id int) error {
	deleteTodo := `delete from todo where id = ?`

	statement, err := store.DB.Prepare(deleteTodo)
	if err != nil {
		return err
	}
	defer statement.Close()

	_, err = statement.Exec(id)
	if err != nil {
		return err
	}

	return nil
}
