package main

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

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

type notebookForm struct {
	Id          int64  `form:"Id"`
	Title       string `form:"Title"`
	Icon        string `form:"Icon"`
	Description string `form:"Description"`
	Fields      []models.Field
	Widgets     []models.Widget
	Validator   validator.Validator `form:"-"`
}

func (form *notebookForm) 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")

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

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

		if err != nil {
			app.serverError(w, r, err)
		}
		fullBuf.WriteTo(w)

		w.WriteHeader(http.StatusUnprocessableEntity)
		return false
	}

	return true
}

func (app *application) notebooks(w http.ResponseWriter, r *http.Request) {
	data := app.newTemplateData(r)
	notebookModel := &models.NotebookModel{DB: app.db}
	var fullBuf = new(bytes.Buffer)

	rows, _, _ := notebookModel.All()
	data["notebooks"] = &rows
	if r.Header.Get("HX-Request") == "true" {

		err := response.HXFragment(fullBuf, []string{"notebooks/list.tmpl"}, "notebooks: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{"notebooks/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{"notebooks/index.tmpl", "notebooks/list.tmpl", "notebooks/list_title.tmpl"})
		if err != nil {
			app.serverError(w, r, err)
		}
	}
	w.WriteHeader(http.StatusOK)

}

func (app *application) notebookCreate(w http.ResponseWriter, r *http.Request) {
	notebookModel := &models.NotebookModel{DB: app.db}
	var fullBuf = new(bytes.Buffer)
	var notebookId int64

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

	switch r.Method {
	case http.MethodGet:

		data["notebook"] = notebookForm{
			Title:       "",
			Icon:        "",
			Description: "",
		}

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

			fullBuf.WriteString("<div hx-swap-oob=\"true\" id=\"page-title\">")
			err = response.HXFragmentOOB(fullBuf, []string{"notebooks/create_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{"notebooks/create_title.tmpl", "notebooks/form.tmpl"})
			if err != nil {
				app.serverError(w, r, err)
			}
		}

	case http.MethodPost:
		var notebookFromForm notebookForm

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

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

		notebook := &models.Notebook{
			Title:       notebookFromForm.Title,
			Icon:        notebookFromForm.Icon,
			Description: notebookFromForm.Description,
		}

		notebookId, err = notebookModel.Create(notebook)
		if err != nil {
			app.badRequest(w, err)
			return
		}

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

		data["notebook"] = notebookForm{
			Id:          notebookId,
			Title:       notebook.Title,
			Icon:        notebook.Icon,
			Description: notebook.Description,
		}

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

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

		notebookModel := &models.NotebookModel{DB: app.db}
		data["notebooksList"] = notebookModel.AllAsOptions(true)
		err = response.HXFragmentOOB(fullBuf, []string{"partials/notebooks-list.tmpl"}, "partial:notebooks-list", data, "current_notebook_id")
		if err != nil {
			app.serverError(w, r, err)
		}

		dataMessage := make(map[string]string)
		dataMessage["messageType"] = "success"
		dataMessage["messageContent"] = "Notebook 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("/notebook/update/", notebookId))
		fullBuf.WriteTo(w)

		w.WriteHeader(http.StatusUnprocessableEntity)
	}
}

func (app *application) notebookUpdate(w http.ResponseWriter, r *http.Request) {
	notebookModel := &models.NotebookModel{DB: app.db}
	notebookIdStr := flow.Param(r.Context(), "notebook_id")
	notebookId, _ := strconv.ParseInt(notebookIdStr, 10, 64)
	notebook, _, _ := notebookModel.One(notebookId)

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

	var fullBuf = new(bytes.Buffer)

	switch r.Method {
	case http.MethodGet:

		data["notebook"] = notebookForm{
			Id:          notebook.Id,
			Title:       notebook.Title,
			Icon:        notebook.Icon,
			Description: notebook.Description,
		}

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

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

			err = response.HXFragmentOOB(fullBuf, []string{"notebooks/update_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{"notebooks/update_title.tmpl", "notebooks/form.tmpl"})
			if err != nil {
				app.serverError(w, r, err)
			}
		}

	case http.MethodPost:
		var notebookFromForm notebookForm

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

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

		notebook.Title = notebookFromForm.Title
		notebook.Description = notebookFromForm.Description
		notebook.Icon = notebookFromForm.Icon

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

		data["notebook"] = notebookForm{
			Id:          notebook.Id,
			Title:       notebook.Title,
			Icon:        notebook.Icon,
			Description: notebook.Description,
		}

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

		fullBuf.WriteString("<div hx-swap-oob=\"true\" id=\"page-title\">")
		err = response.HXFragmentOOB(fullBuf, []string{"notebooks/update_title.tmpl"}, "page:title", data, "page-title")
		if err != nil {
			app.serverError(w, r, err)
		}

		notebookModel := &models.NotebookModel{DB: app.db}
		data["notebooksList"] = notebookModel.AllAsOptions(true)
		err = response.HXFragmentOOB(fullBuf, []string{"partials/notebooks-list.tmpl"}, "partial:notebooks-list", data, "current_notebook_id")
		if err != nil {
			app.serverError(w, r, err)
		}

		fullBuf.WriteTo(w)

		w.WriteHeader(http.StatusUnprocessableEntity)
	}
}

func (app *application) notebookDelete(w http.ResponseWriter, r *http.Request) {
	notebookModel := &models.NotebookModel{DB: app.db}
	notebookIdStr := flow.Param(r.Context(), "notebook_id")
	notebookId, _ := strconv.ParseInt(notebookIdStr, 10, 64)

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