package models

import (
	"database/sql"
	"errors"
	"net/http"
	"strconv"
	"strings"

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

type TypeModel struct {
	DB *database.DB
}

type Type struct {
	Id              int64  `db:"id"`
	Title           string `db:"title"`
	Icon            string `db:"icon"`
	Description     string `db:"description"`
	Notebooks       string `db:"notebooks"`
	ShowSummary     int    `db:"show_summary"`
	ShowDescription int    `db:"show_description"`
	Sections        string `db:"sections"`
}

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

	var row Type

	query := `SELECT * FROM bm_type 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 *TypeModel) Find(criteria map[string]any) ([]Type, bool, error) {
	ctx, cancel := database.GetContext()
	defer cancel()

	var params []interface{}
	var conditions []string
	var cond string
	query := "SELECT DISTINCT bmt.* FROM bm_type bmt "

	for field, value := range criteria {
		switch field {
		case "Title":
			valstr := value.(string)
			if len(valstr) > 0 {
				params = append(params, valstr)
				conditions = append(conditions, "bmt.title LIKE '%' || ? || '%'")
			}
		case "notebook_id":
			if value != nil {
				valint := value.(int64)
				if valint > 0 {
					valstr := "|" + strconv.FormatInt(valint, 10) + "|"
					params = append(params, valstr)
					conditions = append(conditions, "INSTR(notebooks, ?) > 0")
				}
			}
		}
	}

	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 bmt.title`

	var rows []Type

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

	return rows, true, err
}

func (model *TypeModel) FindAsOptions(criteria map[string]any) []funcs.WidgetOption {
	Types, _, _ := model.Find(criteria)
	var selectOptions []funcs.WidgetOption
	for _, Type := range Types {
		selectOptions = append(selectOptions, funcs.WidgetOption{Key: strconv.FormatInt(Type.Id, 10), Value: Type.Title})
	}
	return selectOptions
}

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

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

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

	return true, err
}

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

	query := `INSERT INTO bm_type (title, icon, description, notebooks, sections, show_summary, show_description) 
	VALUES (:title, :icon, :description, :notebooks, :sections, :show_summary, :show_description)`

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

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

	return id, err
}

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

	query := `UPDATE bm_type SET title=:title, icon=:icon, description=:description, notebooks=:notebooks,
	sections=:sections, show_summary=:show_summary, show_description=:show_description WHERE id = :id`

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

	return err
}

func (model *TypeModel) SaveField(field *Field) error {
	ctx, cancel := database.GetContext()
	defer cancel()

	query := `UPDATE bm_type_fields SET widget_id=:widget_id, title=:title, position=:position, 
	valid_values=:valid_values, show_on_list=:show_on_list, show_on_view=:show_on_view, ui_section=:ui_section, is_multiple=:is_multiple WHERE id =:type_field_id`

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

	return err
}

func (model *TypeModel) RemoveField(typeFieldId int64) error {
	ctx, cancel := database.GetContext()
	defer cancel()

	//Check here if it is used
	query := "DELETE FROM bm_type_fields WHERE id=$1"

	_, err := model.DB.ExecContext(ctx, query, typeFieldId)

	return err
}

func (model *TypeModel) AddField(bmField *Field) error {
	ctx, cancel := database.GetContext()
	defer cancel()

	query := `INSERT INTO bm_type_fields (type_id, widget_id, title, position, valid_values, show_on_list, show_on_view, ui_section, is_multiple) 
	VALUES (:type_id, :widget_id, :title, :position, :valid_values, :show_on_list, :show_on_view, :ui_section, :is_multiple)`

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

	return err
}

func (model *TypeModel) AddFields(TypeId int64, r *http.Request) {
	fieldsNew := make(map[int64]Field)
	for name, values := range r.PostForm {
		s, found := strings.CutPrefix(name, "Fields-New-")
		if found {
			parts := strings.Split(s, "-")
			if len(parts) == 2 {
				counter, _ := strconv.ParseInt(parts[0], 10, 64)
				typeFieldAttribute := parts[1]

				field, found := fieldsNew[counter]
				if !found {
					field := &Field{TypeId: TypeId}
					fieldsNew[counter] = *field
				}
				field = fieldsNew[counter]

				switch typeFieldAttribute {
				case "Widget_id":
					field.WidgetId, _ = strconv.ParseInt(values[0], 10, 64)
				case "Title":
					field.Title = values[0]
				case "Valid_values":
					field.ValidValues = values[0]
				case "Ui_section":
					field.UiSection = values[0]
				case "Is_multiple":
					field.IsMultiple, _ = strconv.ParseInt(values[0], 10, 64)
				case "Show_on_list":
					field.ShowOnList, _ = strconv.ParseInt(values[0], 10, 64)
				case "Show_on_view":
					field.ShowOnView, _ = strconv.ParseInt(values[0], 10, 64)
				}
				fieldsNew[counter] = field
			}
		}
	}

	for _, field := range fieldsNew {
		if len(field.Title) > 0 {
			model.AddField(&field)
		}
	}

}
