package issue

import (
	"context"
	"net/http"
	"strings"

	"forge.cadoles.com/wpetit/clearcase/internal/core/service"
	httpCtx "forge.cadoles.com/wpetit/clearcase/internal/http/context"
	"forge.cadoles.com/wpetit/clearcase/internal/http/form"
	"forge.cadoles.com/wpetit/clearcase/internal/http/handler/webui/common"
	"forge.cadoles.com/wpetit/clearcase/internal/http/handler/webui/issue/component"
	"forge.cadoles.com/wpetit/clearcase/internal/http/url"
	"github.com/a-h/templ"
	"github.com/pkg/errors"
)

func (h *Handler) getIssuePage(w http.ResponseWriter, r *http.Request) {
	vmodel, err := h.fillIssuePageVModel(r)
	if err != nil {
		h.handleError(w, r, errors.WithStack(err))
		return
	}

	issue := component.IssuePage(*vmodel)
	templ.Handler(issue).ServeHTTP(w, r)
}

func (h *Handler) fillIssuePageVModel(r *http.Request) (*component.IssuePageVModel, error) {
	vmodel := &component.IssuePageVModel{
		SummaryForm: component.NewIssueSummaryForm(),
		IssueForm:   component.NewIssueForm(),
	}

	err := common.FillViewModel(
		r.Context(), vmodel, r,
		h.fillIssuePageProjects,
		h.fillIssuePageSelectedProject,
	)
	if err != nil {
		return nil, errors.WithStack(err)
	}

	return vmodel, nil
}

func (h *Handler) fillIssuePageProjects(ctx context.Context, vmodel *component.IssuePageVModel, r *http.Request) error {
	user := httpCtx.User(ctx)

	projects, err := h.forge.GetUserProjects(ctx, user)
	if err != nil {
		return errors.WithStack(err)
	}

	vmodel.Projects = projects

	return nil
}

func (h *Handler) fillIssuePageSelectedProject(ctx context.Context, vmodel *component.IssuePageVModel, r *http.Request) error {
	project := r.URL.Query().Get("project")
	if project == "" {
		return nil
	}

	vmodel.SelectedProjectID = project
	vmodel.SummaryForm.Field("project").Set("value", project)

	return nil
}

func (h *Handler) handleIssueSummaryForm(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()

	issueSummaryForm := component.NewIssueSummaryForm()

	if err := issueSummaryForm.Handle(r); err != nil {
		h.handleError(w, r, errors.WithStack(err))
		return
	}

	vmodel, err := h.fillIssuePageVModel(r)
	if err != nil {
		h.handleError(w, r, errors.WithStack(err))
		return
	}

	vmodel.SummaryForm = issueSummaryForm

	if errs := issueSummaryForm.Validate(); errs != nil {
		page := component.IssuePage(*vmodel)
		templ.Handler(page).ServeHTTP(w, r)

		return
	}

	projectID, err := form.FormFieldAttr[string](issueSummaryForm, "project", "value")
	if err != nil {
		h.handleError(w, r, errors.WithStack(err))
		return
	}

	issueSummary, err := form.FormFieldAttr[string](issueSummaryForm, "summary", "value")
	if err != nil {
		h.handleError(w, r, errors.WithStack(err))
		return
	}

	issueTemplate, err := form.FormFieldAttr[string](issueSummaryForm, "template", "value")
	if err != nil {
		h.handleError(w, r, errors.WithStack(err))
		return
	}

	issueTemplate = strings.TrimSpace(issueTemplate)

	issueTitle, issueBody, issueTips, err := h.forge.GenerateIssue(ctx, httpCtx.User(ctx), projectID, issueSummary, issueTemplate)
	if err != nil {
		h.handleError(w, r, errors.WithStack(err))
		return
	}

	vmodel.IssueTips = issueTips
	vmodel.IssueForm.Field("title").Set("value", issueTitle)
	vmodel.IssueForm.Field("body").Set("value", issueBody)

	page := component.IssuePage(*vmodel)
	templ.Handler(page).ServeHTTP(w, r)
}

func (h *Handler) handleIssueForm(w http.ResponseWriter, r *http.Request) {
	issueForm := component.NewIssueForm()

	if err := issueForm.Handle(r); err != nil {
		h.handleError(w, r, errors.WithStack(err))
		return
	}

	vmodel, err := h.fillIssuePageVModel(r)
	if err != nil {
		h.handleError(w, r, errors.WithStack(err))
		return
	}

	if errs := issueForm.Validate(); errs != nil {
		vmodel.IssueForm = issueForm

		page := component.IssuePage(*vmodel)
		templ.Handler(page).ServeHTTP(w, r)

		return
	}

	projectID := r.URL.Query().Get("project")

	title, err := form.FormFieldAttr[string](issueForm, "title", "value")
	if err != nil {
		h.handleError(w, r, errors.WithStack(err))
		return
	}

	body, err := form.FormFieldAttr[string](issueForm, "body", "value")
	if err != nil {
		h.handleError(w, r, errors.WithStack(err))
		return
	}

	ctx := r.Context()
	user := httpCtx.User(ctx)

	issueURL, err := h.forge.CreateIssue(ctx, user, projectID, title, body)
	if err != nil {
		h.handleError(w, r, errors.WithStack(err))
		return
	}

	vmodel.IssueURL = issueURL

	vmodel.SummaryForm.Field("summary").Set("value", "")
	vmodel.IssueForm.Field("title").Set("value", "")
	vmodel.IssueForm.Field("body").Set("value", "")

	page := component.IssuePage(*vmodel)
	templ.Handler(page).ServeHTTP(w, r)
}

func (h *Handler) handleError(w http.ResponseWriter, r *http.Request, err error) {
	if errors.Is(err, service.ErrForgeNotAvailable) {
		baseURL := url.Mutate(httpCtx.BaseURL(r.Context()), url.WithPath("/auth/logout"))
		http.Redirect(w, r, baseURL.String(), http.StatusSeeOther)
		return
	}

	common.HandleError(w, r, errors.WithStack(err))
}