package models

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

	"brainminder.speedtech.it/internal/database"
	"brainminder.speedtech.it/internal/funcs"
	"github.com/jmoiron/sqlx"
)

type CategoryModel struct {
	DB *database.DB
}

type Category struct {
	Id   int64  `db:"id"`
	Name string `db:"name"`
}

func (model *CategoryModel) One(id int) (*Category, bool, error) {
	ctx, cancel := database.GetContext()
	defer cancel()

	var row Category

	query := `SELECT id, name FROM bm_category 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 *CategoryModel) All() ([]Category, bool, error) {
	ctx, cancel := database.GetContext()
	defer cancel()

	query := `SELECT id, name FROM bm_category ORDER BY name`

	var rows []Category

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

	return rows, true, err
}

func (model *CategoryModel) Find(ids []int64) ([]Category, bool, error) {
	ctx, cancel := database.GetContext()
	defer cancel()

	query, args, _ := sqlx.In("SELECT id, name FROM bm_category WHERE id IN (?) ORDER BY name", ids)
	query = model.DB.Rebind(query)

	var rows []Category

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

	return rows, true, err
}

func (model *CategoryModel) AllAsOptions() []funcs.WidgetOption {
	categories, _, _ := model.All()
	var selectOptions []funcs.WidgetOption
	for _, category := range categories {
		selectOptions = append(selectOptions, funcs.WidgetOption{Key: strconv.FormatInt(category.Id, 10), Value: category.Name})
	}
	return selectOptions
}

func (model *CategoryModel) AllAsMap() map[string]string {
	categories, _, _ := model.All()
	var categoriesMap = make(map[string]string)
	for _, category := range categories {
		categoriesMap[strconv.FormatInt(category.Id, 10)] = category.Name
	}
	return categoriesMap
}

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

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

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

	return true, err
}

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

	query := `INSERT INTO bm_category (name) VALUES (:name)`

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

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

	return id, err
}

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

	query := `UPDATE bm_category SET name=:name WHERE id = :id`

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

	return err
}
