package main

import (
	"bytes"
	"fmt"
	"net/http"
	"strconv"
	"strings"

	"brainminder.speedtech.it/internal/request"
	"brainminder.speedtech.it/internal/response"
	"brainminder.speedtech.it/internal/validator"
	"brainminder.speedtech.it/models"
	"github.com/alexedwards/flow"
)

type itemForm struct {
	Id                    int64    `form:"Id"`
	Type_id               int64    `form:"Type_id"`
	Title                 string   `form:"Title"`
	Summary               string   `form:"Summary"`
	Description           string   `form:"Description"`
	Notebooks             []string `form:"Notebooks"`
	Tags                  string   `form:"Tags"`
	On_dashboard          int      `form:"On_dashboard"`
	Categories            []string `form:"Categories"`
	Type_icon             string
	Type_title            string
	Type_show_summary     int
	Type_show_description int
	FieldsSection         map[string][]models.Field
	FieldsValues          map[int64]map[int]string
	Relations             []models.ItemRelation
	Validator             validator.Validator `form:"-"`
}

type itemShareForm struct {
	Id              int64               `form:"Id"`
	Item_id         int64               `form:"Item_id"`
	Token           string              `form:"Token"`
	Start_datetime  string              `form:"start_datetime"`
	End_datetime    string              `form:"end_datetime"`
	Password        string              `form:"password"`
	Permission_read int                 `form:"read"`
	Permission_edit int                 `form:"edit"`
	Validator       validator.Validator `form:"-"`
}

func (form *itemForm) Validate(w http.ResponseWriter, r *http.Request, app *application, data map[string]any) bool {
	var fullBuf = new(bytes.Buffer)

	form.Validator.CheckField(form.Title != "", "Title", "Title is required")
	//form.Validator.CheckField(form.Description != "", "Description", "Description is required")

	if form.Validator.HasErrors() {
		w.Header().Add("HX-Retarget", "#message")
		w.Header().Add("HX-Trigger-After-Settle", `{"showMessage":""}`)

		data["messageType"] = "failure"
		data["messageContent"] = "Impossible to save the item"
		data["messageFieldErrors"] = form.Validator.FieldErrors

		err := response.HXFragment(fullBuf, []string{"partials/message.tmpl"}, "message", data)
		if err != nil {
			app.serverError(w, r, err)
		}
		fullBuf.WriteTo(w)

		w.WriteHeader(http.StatusUnprocessableEntity)
		return false
	}

	return true
}

func (form *itemShareForm) Validate(w http.ResponseWriter, r *http.Request, app *application, data map[string]any) bool {
	var fullBuf = new(bytes.Buffer)

	//form.Validator.CheckField(form.Title != "", "Title", "Title is required")
	//form.Validator.CheckField(form.Description != "", "Description", "Description is required")

	if form.Validator.HasErrors() {
		w.Header().Add("HX-Retarget", "#message")
		w.Header().Add("HX-Trigger-After-Settle", `{"showMessage":""}`)

		data["messageType"] = "failure"
		data["messageContent"] = "Impossible to save the item"
		data["messageFieldErrors"] = form.Validator.FieldErrors

		err := response.HXFragment(fullBuf, []string{"partials/message.tmpl"}, "message", data)
		if err != nil {
			app.serverError(w, r, err)
		}
		fullBuf.WriteTo(w)

		w.WriteHeader(http.StatusUnprocessableEntity)
		return false
	}

	return true
}

func (app *application) itemsRelationAdd(w http.ResponseWriter, r *http.Request) {
	data := app.newTemplateData(r)
	itemModel := models.NewItemModel(app.db)
	categoryModel := &models.CategoryModel{DB: app.db}
	var fullBuf = new(bytes.Buffer)
	related_item_id, _ := strconv.ParseInt(flow.Param(r.Context(), "related_item_id"), 10, 64)

	relatedItem, _, _ := itemModel.One(related_item_id)
	data["relatedItem"] = relatedItem
	data["categoriesMap"] = categoryModel.AllAsMap()

	err := response.HXFragment(fullBuf, []string{"items/add_relation.tmpl"}, "item:add_relation", data)
	if err != nil {
		app.serverError(w, r, err)
	}

	fullBuf.WriteTo(w)
	w.WriteHeader(http.StatusOK)
}

func (app *application) itemsSearch(w http.ResponseWriter, r *http.Request) {
	data := app.newTemplateData(r)
	itemModel := models.NewItemModel(app.db)
	var fullBuf = new(bytes.Buffer)

	switch r.Method {
	case http.MethodPost:
		categoryModel := &models.CategoryModel{DB: app.db}
		data["categoriesMap"] = categoryModel.AllAsMap()
		data["offset"] = 0

		err := r.ParseForm()
		if err != nil {
			app.serverError(w, r, err)
			return
		}

		criteria := make(map[string]any)
		criteria["notebook_id"] = app.getCurrentNotebok_id(r)

		rows, _, _ := itemModel.Search(r.PostForm.Get("SearchText"), criteria)
		data["items"] = &rows
		err = response.HXFragment(fullBuf, []string{"items/all_list.tmpl", "items/all_list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl"}, "itemsAll:list", data)
		if err != nil {
			app.serverError(w, r, err)
		}

		if r.Header.Get("HX-Target") == "page-content" {
			err = response.HXFragmentOOB(fullBuf, []string{"items/all_list_title.tmpl"}, "page:title", data, "page-title")
			if err != nil {
				app.serverError(w, r, err)
			}
		}

		fullBuf.WriteTo(w)
	}
	w.WriteHeader(http.StatusOK)
}

func (app *application) itemsSearchForRelations(w http.ResponseWriter, r *http.Request) {

	data := app.newTemplateData(r)
	itemModel := models.NewItemModel(app.db)
	var fullBuf = new(bytes.Buffer)

	categoryModel := &models.CategoryModel{DB: app.db}
	data["categoriesMap"] = categoryModel.AllAsMap()
	data["item_id"], _ = strconv.ParseInt(flow.Param(r.Context(), "item_id"), 10, 64)

	err := r.ParseForm()
	if err != nil {
		app.serverError(w, r, err)
		return
	}

	criteria := make(map[string]any)
	criteria["notebook_id"] = app.getCurrentNotebok_id(r)

	rows, _, _ := itemModel.Search(r.Form.Get("SearchText"), criteria)
	data["items"] = &rows
	err = response.HXFragment(fullBuf, []string{"items/list_for_relations.tmpl"}, "items:list-for-relations", data)
	if err != nil {
		app.serverError(w, r, err)
	}

	fullBuf.WriteTo(w)
	w.WriteHeader(http.StatusOK)
}

func (app *application) items(w http.ResponseWriter, r *http.Request) {
	data := app.newTemplateData(r)
	itemModel := models.NewItemModel(app.db)
	var fullBuf = new(bytes.Buffer)

	categoryModel := &models.CategoryModel{DB: app.db}
	data["categoriesMap"] = categoryModel.AllAsMap()

	params := r.URL.Query()

	offset_str := r.URL.Query().Get("offset")
	if len(offset_str) == 0 {
		offset_str = "0"
	}
	offset, _ := strconv.ParseInt(offset_str, 10, 64)
	data["offset"] = offset

	switch r.Method {
	case http.MethodGet:

		criteria := make(map[string]any)

		criteriaName := "itemsSearch"
		data["criteriaName"] = criteriaName

		criteriaParam := r.URL.Query()["criteriaName"]
		if len(criteriaParam) > 0 {
			if criteriaParam[0] == criteriaName {
				criteria_values := app.getSessionValue(w, r, criteriaName)
				if criteria_values != nil {
					criteria = criteria_values.(map[string]any)
				}
			}
		} else {
			app.removeSessionValue(w, r, criteriaName)
		}

		criteria["notebook_id"] = app.getCurrentNotebok_id(r)

		rows, _, _ := itemModel.Find(criteria, offset)

		data["items"] = &rows

		if r.Header.Get("HX-Request") == "true" {

			out := params.Get("out")

			if out == "rows" {
				err := response.HXFragment(fullBuf, []string{"items/all_list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl"}, "itemsAll:list_rows", data)
				if err != nil {
					app.serverError(w, r, err)
				}
			} else {
				err := response.HXFragment(fullBuf, []string{"items/all_list.tmpl", "items/all_list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl"}, "itemsAll:list", data)
				if err != nil {
					app.serverError(w, r, err)
				}

				err = response.HXFragmentOOB(fullBuf, []string{"items/all_list_title.tmpl"}, "page:title", data, "page-title")
				if err != nil {
					app.serverError(w, r, err)
				}
			}

			fullBuf.WriteTo(w)
		} else {
			err := response.Page(w, http.StatusOK, data, []string{"items/index_all.tmpl", "items/all_list.tmpl", "items/all_list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl", "items/all_list_title.tmpl"})
			if err != nil {
				app.serverError(w, r, err)
			}
		}
	case http.MethodPost:
		err := r.ParseForm()
		if err != nil {
			app.serverError(w, r, err)
			return
		}

		category_id, _ := strconv.ParseInt(r.PostForm.Get("category_id"), 10, 64)
		criteria := map[string]any{
			"Title":       r.PostForm.Get("Title"),
			"Tags":        r.PostForm.Get("Tags"),
			"category_id": category_id,
		}

		criteriaName := "itemsSearch"
		data["criteriaName"] = criteriaName
		app.saveSessionValue(w, r, criteriaName, criteria)

		rows, _, _ := itemModel.Find(criteria, offset)
		data["items"] = &rows
		err = response.HXFragment(fullBuf, []string{"items/all_list.tmpl", "items/all_list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl"}, "itemsAll:list", data)
		if err != nil {
			app.serverError(w, r, err)
		}

		if r.Header.Get("HX-Target") == "page-content" {
			err = response.HXFragmentOOB(fullBuf, []string{"items/all_list_title.tmpl"}, "page:title", data, "page-title")
			if err != nil {
				app.serverError(w, r, err)
			}
		}

		fullBuf.WriteTo(w)
	}
	w.WriteHeader(http.StatusOK)
}

func (app *application) itemsType(w http.ResponseWriter, r *http.Request) {
	data := app.newTemplateData(r)

	type_id, _ := strconv.ParseInt(flow.Param(r.Context(), "type_id"), 10, 64)

	itemModel := models.NewItemModel(app.db)
	typeModel := &models.TypeModel{DB: app.db}
	aType, _, _ := typeModel.One(type_id)
	fieldModel := &models.FieldModel{DB: app.db}

	fields, _, _ := fieldModel.ByTypeOnList(type_id)
	data["type"] = &aType
	data["Fields"] = fields

	categoryModel := &models.CategoryModel{DB: app.db}
	data["categoriesMap"] = categoryModel.AllAsMap()

	params := r.URL.Query()

	offset_str := params.Get("offset")
	if len(offset_str) == 0 {
		offset_str = "0"
	}
	offset, _ := strconv.ParseInt(offset_str, 10, 64)
	data["offset"] = offset

	var fullBuf = new(bytes.Buffer)

	switch r.Method {
	case http.MethodGet:

		criteria := make(map[string]any)

		criteriaName := fmt.Sprintf("itemsTypeSearch_%d", type_id)
		data["criteriaName"] = criteriaName

		criteriaParam := r.URL.Query()["criteriaName"]
		if len(criteriaParam) > 0 {
			if criteriaParam[0] == criteriaName {
				criteria_values := app.getSessionValue(w, r, criteriaName)
				if criteria_values != nil {
					criteria = criteria_values.(map[string]any)
				}
			}
		} else {
			app.removeSessionValue(w, r, criteriaName)
		}

		criteria["type_id"] = type_id
		criteria["notebook_id"] = app.getCurrentNotebok_id(r)

		rows, _, _ := itemModel.Find(criteria, offset)
		for i, row := range rows {
			rows[i].FieldsValuesMap = fieldModel.GetFieldsValuesAsMap(row.Id)
		}

		data["items"] = rows

		if r.Header.Get("HX-Request") == "true" {
			out := params.Get("out")

			if out == "rows" {
				err := response.HXFragment(fullBuf, []string{"items/list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl"}, "items:list_rows", data)
				if err != nil {
					app.serverError(w, r, err)
				}
			} else {
				err := response.HXFragment(fullBuf, []string{"items/list.tmpl", "items/list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl"}, "items:list", data)
				if err != nil {
					app.serverError(w, r, err)
				}

				fullBuf.WriteString("<div hx-swap-oob=\"true\" id=\"page-title\">")
				err = response.HXFragmentOOB(fullBuf, []string{"items/list_title.tmpl"}, "page:title", data, "page-title")
				if err != nil {
					app.serverError(w, r, err)
				}
			}
			fullBuf.WriteTo(w)
		} else {
			err := response.Page(w, http.StatusOK, data, []string{"items/index.tmpl", "items/list.tmpl", "items/list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl", "items/list_title.tmpl"})
			if err != nil {
				app.serverError(w, r, err)
			}
		}
	case http.MethodPost:
		err := r.ParseForm()
		if err != nil {
			app.serverError(w, r, err)
			return
		}

		category_id, _ := strconv.ParseInt(r.PostForm.Get("category_id"), 10, 64)
		criteria := map[string]any{
			"Title":       r.PostForm.Get("Title"),
			"Tags":        r.PostForm.Get("Tags"),
			"category_id": category_id,
			"type_id":     type_id,
		}

		criteriaName := fmt.Sprintf("itemsTypeSearch_%d", type_id)
		data["criteriaName"] = criteriaName
		app.saveSessionValue(w, r, criteriaName, criteria)

		rows, _, _ := itemModel.Find(criteria, offset)
		for i, row := range rows {
			rows[i].FieldsValuesMap = fieldModel.GetFieldsValuesAsMap(row.Id)
		}
		data["items"] = &rows
		err = response.HXFragment(fullBuf, []string{"items/list.tmpl", "items/list_rows.tmpl", "items/add_to_dashboard.tmpl", "items/remove_from_dashboard.tmpl"}, "items:list", data)
		if err != nil {
			app.serverError(w, r, err)
		}
		fullBuf.WriteTo(w)
	}

	w.WriteHeader(http.StatusOK)
}

func (app *application) itemCreate(w http.ResponseWriter, r *http.Request) {
	itemModel := models.NewItemModel(app.db)
	fieldModel := &models.FieldModel{DB: app.db}
	categoryModel := &models.CategoryModel{DB: app.db}
	typeModel := &models.TypeModel{DB: app.db}

	type_id, _ := strconv.ParseInt(flow.Param(r.Context(), "type_id"), 10, 64)

	aType, _, _ := typeModel.One(type_id)

	data := app.newTemplateData(r)
	data["type"] = aType
	data["formAction"] = fmt.Sprint("/item/create/", type_id)
	data["formTarget"] = "#page-content"
	data["categories"] = categoryModel.AllAsOptions()
	data["categoriesMap"] = categoryModel.AllAsMap()

	criteria := map[string]any{
		"notebook_id": app.getCurrentNotebok_id(r),
	}
	data["types"] = typeModel.FindAsOptions(criteria)

	notebookModel := &models.NotebookModel{DB: app.db}
	data["notebooks"] = notebookModel.AllAsOptions(false)

	var fullBuf = new(bytes.Buffer)
	var categories []string

	fieldsSection, _, _ := fieldModel.ByTypeSection(int64(type_id))
	fieldsValues := make(map[int64]map[int]string)
	switch r.Method {
	case http.MethodGet:
		notebooks := []string{strconv.FormatInt(app.getCurrentNotebok_id(r), 10)}

		data["item"] = itemForm{
			Title:                 "",
			Type_id:               type_id,
			Type_show_summary:     aType.Show_summary,
			Type_show_description: aType.Show_description,
			Summary:               "",
			Description:           "",
			Tags:                  "",
			FieldsSection:         fieldsSection,
			FieldsValues:          fieldsValues,
			Categories:            categories,
			Notebooks:             notebooks,
			Relations:             nil,
		}

		if r.Header.Get("HX-Request") == "true" {

			err := response.HXFragment(fullBuf, []string{"items/form.tmpl", "items/fields.tmpl", "items/relations.tmpl"}, "page:content", data)
			if err != nil {
				app.serverError(w, r, err)
			}

			err = response.HXFragmentOOB(fullBuf, []string{"items/create_title.tmpl"}, "page:title", data, "page-title")
			if err != nil {
				app.serverError(w, r, err)
			}

			w.Header().Add("HX-Trigger-After-Settle", `{"activateEasyMDE":"item-description"}`)
			fullBuf.WriteTo(w)
		} else {
			err := response.Page(w, http.StatusOK, data, []string{"items/create_title.tmpl", "items/relations.tmpl", "items/form.tmpl", "items/fields.tmpl", "items/form_jscode.tmpl"})
			if err != nil {
				app.serverError(w, r, err)
			}
		}

	case http.MethodPost:
		var itemFromForm itemForm
		var item_id int64

		err := request.DecodePostForm(r, &itemFromForm)
		if err != nil {
			app.serverError(w, r, err)
		}

		if !itemFromForm.Validate(w, r, app, data) {
			return
		}

		item := &models.Item{
			Type_id:      itemFromForm.Type_id,
			Title:        itemFromForm.Title,
			Summary:      itemFromForm.Summary,
			Description:  itemFromForm.Description,
			Tags:         itemFromForm.Tags,
			On_dashboard: itemFromForm.On_dashboard,
		}

		notebooks_str := strings.Join(itemFromForm.Notebooks, "|")
		if len(notebooks_str) > 0 {
			notebooks_str = "|" + notebooks_str + "|"
		}
		item.Notebooks = notebooks_str

		categories_str := strings.Join(itemFromForm.Categories, "|")
		if len(categories_str) > 0 {
			categories_str = "|" + categories_str + "|"
		}
		item.Categories = categories_str

		item_id, err = itemModel.Create(item)
		item.Id = item_id
		if err != nil {
			app.badRequest(w, err)
			return
		}

		fieldsValues := make(map[int64]map[int]string)
		for name, values := range r.PostForm {
			id_s, found := strings.CutPrefix(name, "FieldsValues-")
			if found {
				parts := strings.Split(id_s, "-")
				type_field_id, _ := strconv.ParseInt(parts[0], 10, 64)
				counter, _ := strconv.Atoi(parts[1])

				_, found_key := fieldsValues[type_field_id]
				if !found_key {
					fieldsValues[type_field_id] = make(map[int]string)
				}
				fieldsValues[type_field_id][counter] = values[0]
			}

			related_id_s, found := strings.CutPrefix(name, "ItemRelation-New-")
			if found && len(values) > 0 {
				related_item_id, _ := strconv.ParseInt(related_id_s, 10, 64)
				itemModel.AddRelation(item.Id, related_item_id, values[0])
			}
		}

		for type_field_id, values := range fieldsValues {
			fieldModel.SaveValues(item.Id, type_field_id, values)
		}

		item.Type_title = aType.Title
		fields, _, _ := fieldModel.ByType(int64(type_id))
		itemModel.SaveKeywords(item, &fields, fieldsValues)

		data["formAction"] = fmt.Sprint("/item/update/", item_id)
		data["formTarget"] = "#message"

		notebooks := strings.Split(strings.Trim(item.Notebooks, "|"), "|")
		categories := strings.Split(strings.Trim(item.Categories, "|"), "|")
		relations, _, _ := itemModel.GetRelations(item.Id)

		data["item"] = itemForm{
			Id:                    item_id,
			Type_id:               type_id,
			Type_show_summary:     aType.Show_summary,
			Type_show_description: aType.Show_description,
			Title:                 item.Title,
			Summary:               item.Summary,
			Description:           item.Description,
			Tags:                  item.Tags,
			Type_icon:             item.Type_icon,
			Type_title:            item.Type_title,
			FieldsSection:         fieldsSection,
			FieldsValues:          fieldsValues,
			Notebooks:             notebooks,
			Categories:            categories,
			Relations:             relations,
		}

		err = response.HXFragment(fullBuf, []string{"items/form.tmpl", "items/fields.tmpl", "items/relations.tmpl"}, "page:content", data)
		if err != nil {
			app.serverError(w, r, err)
		}

		err = response.HXFragmentOOB(fullBuf, []string{"items/update_title.tmpl"}, "page:title", data, "page-title")
		if err != nil {
			app.serverError(w, r, err)
		}

		dataMessage := make(map[string]string)
		dataMessage["messageType"] = "success"
		dataMessage["messageContent"] = "Item created successfully"
		err = response.HXFragmentOOB(fullBuf, []string{"partials/message.tmpl"}, "message", dataMessage, "message")
		if err != nil {
			app.serverError(w, r, err)
		}

		w.Header().Add("HX-Trigger-After-Settle", `{"activateEasyMDE":"item-description","showMessage":""}`)
		w.Header().Add("HX-Replace-Url", fmt.Sprint("/item/update/", item_id))
		fullBuf.WriteTo(w)

		w.WriteHeader(http.StatusUnprocessableEntity)
	}
}

func (app *application) itemRead(w http.ResponseWriter, r *http.Request) {
	data := app.newTemplateData(r)
	item_id, _ := strconv.ParseInt(flow.Param(r.Context(), "item_id"), 10, 64)
	itemModel := models.NewItemModel(app.db)
	categoryModel := &models.CategoryModel{DB: app.db}
	fieldModel := &models.FieldModel{DB: app.db}
	relations, _, _ := itemModel.GetRelations(item_id)

	item, _, _ := itemModel.One(item_id)

	data["item"] = &item
	data["relations"] = relations
	data["categoriesMap"] = categoryModel.AllAsMap()
	data["fields"] = fieldModel.GetFieldsValues(item_id)

	if r.Header.Get("HX-Request") == "true" {
		var fullBuf = new(bytes.Buffer)

		err := response.HXFragment(fullBuf, []string{"items/read.tmpl", "items/relations_view.tmpl"}, "page:content", data)
		if err != nil {
			app.serverError(w, r, err)
		}

		err = response.HXFragmentOOB(fullBuf, []string{"items/read_title.tmpl"}, "page:title", data, "page-title")
		if err != nil {
			app.serverError(w, r, err)
		}

		fullBuf.WriteTo(w)
	} else {
		err := response.Page(w, http.StatusOK, data, []string{"items/read_title.tmpl", "items/relations_view.tmpl", "items/read.tmpl"})
		if err != nil {
			app.serverError(w, r, err)
		}
	}

	w.WriteHeader(http.StatusOK)
}

func (app *application) itemView(w http.ResponseWriter, r *http.Request) {
	data := app.newTemplateData(r)
	item_id, _ := strconv.ParseInt(flow.Param(r.Context(), "item_id"), 10, 64)
	itemModel := models.NewItemModel(app.db)
	categoryModel := &models.CategoryModel{DB: app.db}
	fieldModel := &models.FieldModel{DB: app.db}

	item, _, _ := itemModel.One(item_id)
	relations, _, _ := itemModel.GetRelations(item_id)

	data["item"] = &item
	data["relations"] = relations
	data["categoriesMap"] = categoryModel.AllAsMap()
	data["fields"] = fieldModel.GetFieldsValues(item_id)

	if r.Header.Get("HX-Request") == "true" {
		var fullBuf = new(bytes.Buffer)
		err := response.HXFragment(fullBuf, []string{"items/view.tmpl", "items/relations_view.tmpl"}, "page:content", data)
		if err != nil {
			app.serverError(w, r, err)
		}
		w.Header().Add("HX-Trigger-After-Settle", `{"showModalDialog": ""}`)
		fullBuf.WriteTo(w)
	} else {
		err := response.Page(w, http.StatusOK, data, []string{"items/read_title.tmpl", "items/relations_view.tmpl", "items/read.tmpl"})
		if err != nil {
			app.serverError(w, r, err)
		}

	}
	w.WriteHeader(http.StatusOK)
}

func (app *application) itemUpdate(w http.ResponseWriter, r *http.Request) {
	itemModel := models.NewItemModel(app.db)
	fieldModel := &models.FieldModel{DB: app.db}
	item_id_str := flow.Param(r.Context(), "item_id")
	item_id, _ := strconv.ParseInt(flow.Param(r.Context(), "item_id"), 10, 64)
	item, _, _ := itemModel.One(item_id)
	fieldsSection, _, _ := fieldModel.ByTypeSection(item.Type_id)
	fieldsValues := fieldModel.GetFieldsValuesAsMap(item.Id)

	data := app.newTemplateData(r)
	data["formAction"] = "/item/update/" + item_id_str
	data["formTarget"] = "#message"

	notebookModel := &models.NotebookModel{DB: app.db}
	data["notebooks"] = notebookModel.AllAsOptions(false)

	categoryModel := &models.CategoryModel{DB: app.db}
	data["categoriesMap"] = categoryModel.AllAsMap()

	var fullBuf = new(bytes.Buffer)

	switch r.Method {
	case http.MethodGet:

		notebooks := strings.Split(strings.Trim(item.Notebooks, "|"), "|")
		categories := strings.Split(strings.Trim(item.Categories, "|"), "|")
		relations, _, _ := itemModel.GetRelations(item.Id)

		data["item"] = itemForm{
			Id:                    item.Id,
			Type_id:               item.Type_id,
			Type_show_summary:     item.Type_show_summary,
			Type_show_description: item.Type_show_description,
			Title:                 item.Title,
			Summary:               item.Summary,
			Description:           item.Description,
			Tags:                  item.Tags,
			On_dashboard:          item.On_dashboard,
			Type_icon:             item.Type_icon,
			Type_title:            item.Type_title,
			FieldsSection:         fieldsSection,
			FieldsValues:          fieldsValues,
			Categories:            categories,
			Notebooks:             notebooks,
			Relations:             relations,
		}

		data["categories"] = categoryModel.AllAsOptions()

		typeModel := &models.TypeModel{DB: app.db}
		criteria := map[string]any{
			"notebook_id": app.getCurrentNotebok_id(r),
		}
		data["types"] = typeModel.FindAsOptions(criteria)

		if r.Header.Get("HX-Request") == "true" {

			err := response.HXFragment(fullBuf, []string{"items/form.tmpl", "items/fields.tmpl", "items/relations.tmpl"}, "page:content", data)
			if err != nil {
				app.serverError(w, r, err)
			}

			err = response.HXFragmentOOB(fullBuf, []string{"items/update_title.tmpl"}, "page:title", data, "page-title")
			if err != nil {
				app.serverError(w, r, err)
			}

			w.Header().Add("HX-Trigger-After-Settle", `{"activateEasyMDE":"item-description"}`)
			fullBuf.WriteTo(w)
		} else {
			err := response.Page(w, http.StatusOK, data, []string{"items/update_title.tmpl", "items/form.tmpl", "items/fields.tmpl", "items/relations.tmpl", "items/form_jscode.tmpl"})
			if err != nil {
				app.serverError(w, r, err)
			}
		}

	case http.MethodPost:
		var itemFromForm itemForm

		err := request.DecodePostForm(r, &itemFromForm)
		if err != nil {
			app.serverError(w, r, err)
		}

		if !itemFromForm.Validate(w, r, app, data) {
			return
		}

		item.Title = itemFromForm.Title
		item.Type_id = itemFromForm.Type_id
		item.Summary = itemFromForm.Summary
		item.Description = itemFromForm.Description
		item.Tags = itemFromForm.Tags
		item.On_dashboard = itemFromForm.On_dashboard

		notebooks_str := strings.Join(itemFromForm.Notebooks, "|")
		if len(notebooks_str) > 0 {
			notebooks_str = "|" + notebooks_str + "|"
		}
		item.Notebooks = notebooks_str

		categories_str := strings.Join(itemFromForm.Categories, "|")
		if len(categories_str) > 0 {
			categories_str = "|" + categories_str + "|"
		}
		item.Categories = categories_str

		err = itemModel.Update(item)
		if err != nil {
			app.badRequest(w, err)
			return
		}

		fieldsValues := make(map[int64]map[int]string)
		for name, values := range r.PostForm {
			id_s, found := strings.CutPrefix(name, "FieldsValues-")
			if found {
				parts := strings.Split(id_s, "-")
				type_field_id, _ := strconv.ParseInt(parts[0], 10, 64)
				counter, _ := strconv.Atoi(parts[1])

				valuesRemove, isMapContainsKey := r.PostForm["FieldsValuesToRemove-"+id_s]
				if !isMapContainsKey || valuesRemove[0] == "0" {
					_, found_key := fieldsValues[type_field_id]
					if !found_key {
						fieldsValues[type_field_id] = make(map[int]string)
					}
					fieldsValues[type_field_id][counter] = values[0]
				}
			}
			related_id_s, found := strings.CutPrefix(name, "ItemRelation-New-")
			if found && len(values) > 0 {
				related_item_id, _ := strconv.ParseInt(related_id_s, 10, 64)
				itemModel.AddRelation(item.Id, related_item_id, values[0])
			}

			id_s, found = strings.CutPrefix(name, "ItemRelation-")
			if found && len(values) > 0 {
				ids := strings.Split(id_s, "-")
				item_id, _ := strconv.ParseInt(ids[0], 10, 64)
				related_item_id, _ := strconv.ParseInt(ids[1], 10, 64)
				valuesRemove, isMapContainsKey := r.PostForm["ItemRelation-ToRemove-"+id_s]
				if isMapContainsKey && valuesRemove[0] == "1" {
					itemModel.DeleteRelation(item_id, related_item_id)
				} else {
					itemModel.UpdateRelation(item_id, related_item_id, values[0])
				}
			}
		}

		for type_field_id, values := range fieldsValues {
			fieldModel.SaveValues(item.Id, type_field_id, values)
		}

		fields, _, _ := fieldModel.ByType(item.Type_id)
		itemModel.SaveKeywords(item, &fields, fieldsValues)

		relations, _, _ := itemModel.GetRelations(item.Id)

		data["item"] = itemForm{
			Title:      item.Title,
			Type_icon:  item.Type_icon,
			Type_title: item.Type_title,
			Relations:  relations,
		}

		data["FieldsSection"] = fieldsSection
		data["FieldsValues"] = fieldsValues

		dataMessage := make(map[string]string)
		dataMessage["messageType"] = "success"
		dataMessage["messageContent"] = "Item saved successfully"
		err = response.HXFragment(fullBuf, []string{"partials/message.tmpl"}, "message", dataMessage)
		if err != nil {
			app.serverError(w, r, err)
		}

		err = response.HXFragmentOOB(fullBuf, []string{"items/update_title.tmpl"}, "page:title", data, "page-title")
		if err != nil {
			app.serverError(w, r, err)
		}

		err = response.HXFragmentOOB(fullBuf, []string{"items/relations.tmpl"}, "item:relations", data, "relations")
		if err != nil {
			app.serverError(w, r, err)
		}

		data["uisection"] = "general"
		err = response.HXFragmentOOB(fullBuf, []string{"items/fields.tmpl"}, "items:fields", data, "fields-general")
		if err != nil {
			app.serverError(w, r, err)
		}

		_, found := fieldsSection["fields"]
		if found {
			data["uisection"] = "fields"
			err = response.HXFragmentOOB(fullBuf, []string{"items/fields.tmpl"}, "items:fields", data, "fields-fields")
			if err != nil {
				app.serverError(w, r, err)
			}
		}

		w.Header().Add("HX-Reswap", `beforeend`)
		w.Header().Add("HX-Trigger-After-Settle", `{"showMessage": ""}`)

		fullBuf.WriteTo(w)

		w.WriteHeader(http.StatusUnprocessableEntity)
	}
}

func (app *application) itemDelete(w http.ResponseWriter, r *http.Request) {
	itemModel := models.NewItemModel(app.db)
	item_id_str := flow.Param(r.Context(), "item_id")
	item_id, _ := strconv.Atoi(item_id_str)

	_, err := itemModel.Delete(item_id)
	if err != nil {
		app.serverError(w, r, err)
	}
}

func (app *application) itemShare(w http.ResponseWriter, r *http.Request) {
	itemModel := models.NewItemModel(app.db)
	item_id, _ := strconv.ParseInt(flow.Param(r.Context(), "item_id"), 10, 64)
	item, _, _ := itemModel.One(item_id)

	data := app.newTemplateData(r)
	data["item"] = item
	data["baseUrl"] = app.config.baseURL

	switch r.Method {
	case http.MethodGet:
		var fullBuf = new(bytes.Buffer)

		data["shareToken"] = app.generateSecureToken(18)

		err := response.HXFragment(fullBuf, []string{"items/share.tmpl"}, "page:content", data)
		if err != nil {
			app.serverError(w, r, err)
		}

		w.Header().Add("HX-Trigger-After-Settle", `{"showModalDialog": ""}`)
		fullBuf.WriteTo(w)
	case http.MethodPost:
		var itemShareFromForm itemShareForm

		err := request.DecodePostForm(r, &itemShareFromForm)
		if err != nil {
			app.serverError(w, r, err)
		}

		if !itemShareFromForm.Validate(w, r, app, data) {
			return
		}

		itemShare := &models.ItemShare{
			Item_id: item_id,
			Token:   itemShareFromForm.Token,
		}

		itemshareModel := &models.ItemShareModel{DB: app.db}
		_, err = itemshareModel.Create(itemShare)
		if err != nil {
			app.badRequest(w, err)
			return
		}
	}
}

func (app *application) itemAddToDashboard(w http.ResponseWriter, r *http.Request) {
	itemModel := models.NewItemModel(app.db)
	item_id, _ := strconv.ParseInt(flow.Param(r.Context(), "item_id"), 10, 64)

	err := itemModel.AddToDashboard(item_id)
	if err != nil {
		app.serverError(w, r, err)
	}

	var fullBuf = new(bytes.Buffer)

	err = response.HXFragment(fullBuf, []string{"items/remove_from_dashboard.tmpl"}, "item:remove_from_dashboard", item_id)
	if err != nil {
		app.serverError(w, r, err)
	}

	fullBuf.WriteTo(w)
}

func (app *application) itemRemoveFromDashboard(w http.ResponseWriter, r *http.Request) {
	itemModel := models.NewItemModel(app.db)
	item_id, _ := strconv.ParseInt(flow.Param(r.Context(), "item_id"), 10, 64)

	err := itemModel.RemoveFromDashboard(item_id)
	if err != nil {
		app.serverError(w, r, err)
	}

	if r.Method == http.MethodPost {
		var fullBuf = new(bytes.Buffer)

		err := response.HXFragment(fullBuf, []string{"items/add_to_dashboard.tmpl"}, "item:add_to_dashboard", item_id)
		if err != nil {
			app.serverError(w, r, err)
		}

		fullBuf.WriteTo(w)
	}
}
