package main

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

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

type typeForm struct {
	Id              int64    `form:"Id"`
	Title           string   `form:"Title"`
	Icon            string   `form:"Icon"`
	Description     string   `form:"Description"`
	ShowSummary     int      `form:"Show_summary"`
	ShowDescription int      `form:"Show_description"`
	Notebooks       []string `form:"Notebooks"`
	Fields          []models.Field
	Widgets         []models.Widget
	Validator       validator.Validator `form:"-"`
}

func (form *typeForm) 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 type"
		data["messageFieldErrors"] = form.Validator.FieldErrors

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

		_, err = fullBuf.WriteTo(w)
		if err != nil {
			app.logError("Building type validation response buffer : ", err)
		}

		w.WriteHeader(http.StatusUnprocessableEntity)
		return false
	}

	return true
}

func (app *application) types(w http.ResponseWriter, r *http.Request) {
	data := app.newTemplateData(r)
	typeModel := &models.TypeModel{DB: app.db}

	var fullBuf = new(bytes.Buffer)

	criteria := map[string]any{}
	rows, _, _ := typeModel.Find(criteria)
	data["types"] = &rows
	if r.Header.Get("HX-Request") == "true" {

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

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

		_, err = fullBuf.WriteTo(w)
		if err != nil {
			app.logError("Building types list response buffer : ", err)
		}
	} else {
		err := response.Page(w, http.StatusOK, data, []string{"types/index.tmpl", "types/list.tmpl", "types/list_title.tmpl"})
		if err != nil {
			app.serverError(w, r, err)
		}
	}

	w.WriteHeader(http.StatusOK)
}

func (app *application) typeCreate(w http.ResponseWriter, r *http.Request) {
	typeModel := &models.TypeModel{DB: app.db}
	var fullBuf = new(bytes.Buffer)
	var typeId int64

	data := app.newTemplateData(r)
	data["formAction"] = "/type/create"
	data["formTarget"] = "#page-content"

	widgetModel := &models.WidgetModel{DB: app.db}
	data["widgets"] = widgetModel.AllAsOptions()
	data["uisections"] = widgetModel.UISections()

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

	switch r.Method {
	case http.MethodGet:

		notebooks := []string{strconv.FormatInt(app.getCurrentNotebookId(r), 10)}

		data["type"] = typeForm{
			Title:           "",
			Icon:            "",
			Description:     "",
			ShowSummary:     0,
			ShowDescription: 0,
			Notebooks:       notebooks,
		}

		if r.Header.Get("HX-Request") == "true" {
			err := response.HXFragment(fullBuf, []string{"types/form.tmpl", "types/fields.tmpl"}, "page:content", data)
			if err != nil {
				app.serverError(w, r, err)
			}

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

			_, err = fullBuf.WriteTo(w)
			if err != nil {
				app.logError("Building type create form buffer : ", err)
			}
		} else {
			err := response.Page(w, http.StatusOK, data, []string{"types/create_title.tmpl", "types/fields.tmpl", "types/form.tmpl"})
			if err != nil {
				app.serverError(w, r, err)
			}
		}

	case http.MethodPost:
		var typeFromForm typeForm

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

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

		aType := &models.Type{
			Title:           typeFromForm.Title,
			Icon:            typeFromForm.Icon,
			Description:     typeFromForm.Description,
			ShowSummary:     typeFromForm.ShowSummary,
			ShowDescription: typeFromForm.ShowDescription,
		}
		notebooksStr := strings.Join(typeFromForm.Notebooks, "|")
		if len(notebooksStr) > 0 {
			notebooksStr = "|" + notebooksStr + "|"
		}
		aType.Notebooks = notebooksStr

		typeId, err = typeModel.Create(aType)
		if err != nil {
			app.badRequest(w, err)
			return
		}

		typeModel.AddFields(typeId, r)

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

		fieldModel := &models.FieldModel{DB: app.db}
		fields, _, _ := fieldModel.ByType(typeId)

		data["type"] = typeForm{
			Id:              typeId,
			Title:           aType.Title,
			Icon:            aType.Icon,
			Description:     aType.Description,
			ShowSummary:     aType.ShowSummary,
			ShowDescription: aType.ShowDescription,
			Fields:          fields,
			Notebooks:       typeFromForm.Notebooks,
		}

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

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

		criteria := map[string]any{
			"notebook_id": app.getCurrentNotebookId(r),
		}
		types, _, _ := typeModel.Find(criteria)
		data["typesList"] = types
		err = response.HXFragmentOOB(fullBuf, []string{"partials/types-list.tmpl"}, "partial:types-list", data, "types-list")
		if err != nil {
			app.serverError(w, r, err)
		}

		dataMessage := make(map[string]string)
		dataMessage["messageType"] = "success"
		dataMessage["messageContent"] = "Type 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-Replace-Url", fmt.Sprint("/type/update/", typeId))
		w.Header().Add("HX-Trigger-After-Settle", `{"showMessage":""}`)

		_, err = fullBuf.WriteTo(w)
		if err != nil {
			app.logError("Building notebook create form buffer on post : ", err)
		}

		w.WriteHeader(http.StatusUnprocessableEntity)
	}
}

func (app *application) typeUpdate(w http.ResponseWriter, r *http.Request) {
	typeModel := &models.TypeModel{DB: app.db}
	fieldModel := &models.FieldModel{DB: app.db}
	typeIdStr := flow.Param(r.Context(), "type_id")
	typeId, _ := strconv.ParseInt(typeIdStr, 10, 64)
	aType, _, _ := typeModel.One(typeId)

	data := app.newTemplateData(r)
	data["formAction"] = "/type/update/" + typeIdStr
	data["formTarget"] = "#message"
	data["messageType"] = ""
	data["messageContent"] = ""

	widgetModel := &models.WidgetModel{DB: app.db}
	data["widgets"] = widgetModel.AllAsOptions()
	data["uisections"] = widgetModel.UISections()

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

	var fullBuf = new(bytes.Buffer)

	switch r.Method {
	case http.MethodGet:

		fields, _, _ := fieldModel.ByType(aType.Id)

		notebooks := strings.Split(strings.Trim(aType.Notebooks, "|"), "|")

		data["type"] = typeForm{
			Id:              aType.Id,
			Title:           aType.Title,
			Icon:            aType.Icon,
			Description:     aType.Description,
			ShowSummary:     aType.ShowSummary,
			ShowDescription: aType.ShowDescription,
			Fields:          fields,
			Notebooks:       notebooks,
		}

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

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

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

			_, err = fullBuf.WriteTo(w)
			if err != nil {
				app.logError("Building type update form buffer : ", err)
			}
		} else {
			err := response.Page(w, http.StatusOK, data, []string{"types/update_title.tmpl", "types/form.tmpl", "types/fields.tmpl"})
			if err != nil {
				app.serverError(w, r, err)
			}
		}

	case http.MethodPost:
		var typeFromForm typeForm

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

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

		aType.Title = typeFromForm.Title
		aType.Description = typeFromForm.Description
		aType.Icon = typeFromForm.Icon
		aType.ShowSummary = typeFromForm.ShowSummary
		aType.ShowDescription = typeFromForm.ShowDescription
		notebooksStr := strings.Join(typeFromForm.Notebooks, "|")
		if len(notebooksStr) > 0 {
			notebooksStr = "|" + notebooksStr + "|"
		}
		aType.Notebooks = notebooksStr

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

		fieldsUpdate := make(map[int64]models.Field)
		for name, values := range r.PostForm {
			s, found := strings.CutPrefix(name, "Fields-")
			if found {
				parts := strings.Split(s, "-")
				if len(parts) == 2 {
					typeFieldId, _ := strconv.ParseInt(parts[0], 10, 64)
					typeFieldAttribute := parts[1]

					field, found := fieldsUpdate[typeFieldId]
					if !found {
						field := &models.Field{TypeFieldId: typeFieldId}
						fieldsUpdate[typeFieldId] = *field
					}
					field = fieldsUpdate[typeFieldId]

					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 "Show_on_list":
						field.ShowOnList, _ = strconv.ParseInt(values[0], 10, 64)
					case "Show_on_view":
						field.ShowOnView, _ = strconv.ParseInt(values[0], 10, 64)
					case "Is_multiple":
						field.IsMultiple, _ = strconv.ParseInt(values[0], 10, 64)
					case "ToRemove":
						field.ToRemove, _ = strconv.ParseInt(values[0], 10, 64)
					}
					fieldsUpdate[typeFieldId] = field
				}
			}
		}

		for id, field := range fieldsUpdate {
			if field.ToRemove > 0 {
				err = typeModel.RemoveField(id)
				if err != nil {
					app.logError("Removing a field from a type : ", err)
				}
			} else {
				err = typeModel.SaveField(&field)
				if err != nil {
					app.logError("Adding a field to a type : ", err)
				}
			}
		}

		typeModel.AddFields(typeId, r)

		data["type"] = typeForm{
			Id:              aType.Id,
			Title:           aType.Title,
			Icon:            aType.Icon,
			Description:     aType.Description,
			ShowSummary:     aType.ShowSummary,
			ShowDescription: aType.ShowDescription,
		}

		dataMessage := make(map[string]string)
		dataMessage["messageType"] = "success"
		dataMessage["messageContent"] = "Type 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{"types/update_title.tmpl"}, "page:title", data, "page-title")
		if err != nil {
			app.serverError(w, r, err)
		}

		fieldData := make(map[string]any)
		fields, _, _ := fieldModel.ByType(aType.Id)

		fieldData["fields"] = fields
		fieldData["widgets"] = data["widgets"]
		fieldData["uisections"] = widgetModel.UISections()

		err = response.HXFragmentOOB(fullBuf, []string{"types/fields.tmpl"}, "types:fields", fieldData, "fields-list")
		if err != nil {
			app.serverError(w, r, err)
		}

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

		_, err = fullBuf.WriteTo(w)
		if err != nil {
			app.logError("Building type update form buffer on post : ", err)
		}

		w.WriteHeader(http.StatusUnprocessableEntity)
	}
}

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

	data["counter"] = r.URL.Query()["counter"][0]
	widgetModel := &models.WidgetModel{DB: app.db}
	widgets, _, _ := widgetModel.All()
	var selectOptions []funcs.WidgetOption
	for _, widget := range widgets {
		selectOptions = append(selectOptions, funcs.WidgetOption{Key: strconv.FormatInt(widget.Id, 10), Value: widget.Name})
	}
	data["widgets"] = selectOptions
	data["uisections"] = widgetModel.UISections()

	buf, err := response.Fragment([]string{"types/field_new.tmpl"}, "type:field_new", data)
	if err != nil {
		app.serverError(w, r, err)
	}
	_, err = buf.WriteTo(w)
	if err != nil {
		app.logError("Adding new field to a type buffer : ", err)
	}
}

func (app *application) typeDelete(w http.ResponseWriter, r *http.Request) {
	typeModel := &models.TypeModel{DB: app.db}
	itemIdStr := flow.Param(r.Context(), "type_id")
	itemId, _ := strconv.Atoi(itemIdStr)

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