package models

import (
	"database/sql"
	"errors"
	"strconv"

	"brainminder.speedtech.it/internal/database"
	"brainminder.speedtech.it/internal/funcs"
)

type NotebookModel struct {
	DB *database.DB
}

type Notebook struct {
	Id          int64  `db:"id"`
	Title       string `db:"title"`
	Icon        string `db:"icon"`
	Description string `db:"description"`
	Hidden      int    `db:"hidden"`
}

func (model *NotebookModel) One(id int64) (*Notebook, bool, error) {
	ctx, cancel := database.GetContext()
	defer cancel()

	var row Notebook

	query := `SELECT * FROM bm_notebook WHERE id = $1`

	err := model.DB.GetContext(ctx, &row, query, id)
	if errors.Is(err, sql.ErrNoRows) {
		return nil, false, nil
	}

	return &row, true, err
}

func (model *NotebookModel) All() ([]Notebook, bool, error) {
	ctx, cancel := database.GetContext()
	defer cancel()

	query := `SELECT * FROM bm_notebook ORDER BY title`

	var rows []Notebook

	err := model.DB.SelectContext(ctx, &rows, query)
	if errors.Is(err, sql.ErrNoRows) {
		return nil, false, nil
	}

	return rows, true, err
}

func (model *NotebookModel) AllAsOptions(withAll bool) []funcs.WidgetOption {
	Notebooks, _, _ := model.All()
	var selectOptions []funcs.WidgetOption
	if withAll {
		selectOptions = append(selectOptions, funcs.WidgetOption{Key: "0", Value: "- All -"})
	}
	for _, Notebook := range Notebooks {
		selectOptions = append(selectOptions, funcs.WidgetOption{Key: strconv.FormatInt(Notebook.Id, 10), Value: Notebook.Title})
	}
	return selectOptions
}

func (model *NotebookModel) Find(criteria map[string]any) ([]Notebook, bool, error) {
	ctx, cancel := database.GetContext()
	defer cancel()

	var params []interface{}
	var conditions []string
	var cond string
	query := "SELECT bmn.* FROM bm_notebook bmn "

	for field, value := range criteria {
		switch field {
		case "Title":
			valstr := value.(string)
			if len(valstr) > 0 {
				params = append(params, valstr)
				conditions = append(conditions, "bmn.title LIKE '%' || ? || '%'")
			}
		}
	}

	for _, condition := range conditions {
		if len(cond) > 0 {
			cond = cond + " AND "
		}
		cond = cond + condition
	}

	if len(cond) > 0 {
		query = query + "WHERE " + cond + " "
	}
	query = query + `ORDER BY bmn.title`

	var rows []Notebook

	err := model.DB.SelectContext(ctx, &rows, query, params...)
	if errors.Is(err, sql.ErrNoRows) {
		return nil, false, nil
	}

	return rows, true, err
}

func (model *NotebookModel) Create(Notebook *Notebook) (int64, error) {
	ctx, cancel := database.GetContext()
	defer cancel()

	query := `INSERT INTO bm_notebook (title, description, icon, hidden) VALUES (:title, :description, :icon, :hidden)`

	result, err := model.DB.NamedExecContext(ctx, query, Notebook)
	if err != nil {
		return 0, err
	}

	id, err := result.LastInsertId()
	if err != nil {
		return 0, err
	}

	return id, err
}

func (model *NotebookModel) Update(Notebook *Notebook) error {
	ctx, cancel := database.GetContext()
	defer cancel()

	query := `UPDATE bm_notebook SET title=:title, icon=:icon, description=:description, hidden=:hidden WHERE id = :id`

	_, err := model.DB.NamedExecContext(ctx, query, Notebook)

	return err
}

func (model *NotebookModel) Delete(id int64) (bool, error) {
	ctx, cancel := database.GetContext()
	defer cancel()

	query := `DELETE FROM bm_notebook WHERE id = $1`

	_, err := model.DB.ExecContext(ctx, query, id)
	if errors.Is(err, sql.ErrNoRows) {
		return false, nil
	}

	return true, err
}
