package httpserver

import (
	"errors"
	"fmt"
	"net/http"

	"update_server/internal/db"
)

func (h *handler) adminTags(w http.ResponseWriter, r *http.Request) {
	tags, err := h.store.Tags.List(r.Context())
	if err != nil {
		http.Error(w, "tag listing failed", http.StatusInternalServerError)
		return
	}

	data := TagsPageData{
		PageData: h.basePageData(
			r,
			"Tags",
			"Protected Admin",
			"Tags",
			"Create reusable tags now so projects can be grouped for future access rules and release discovery endpoints.",
		),
		Tags: tags,
	}
	data.Flash = tagListFlash(r.URL.Query().Get("status"))

	renderPage(w, h.renderer, "tags", data)
}

func (h *handler) adminTagNew(w http.ResponseWriter, r *http.Request) {
	data := TagFormPageData{
		PageData: h.basePageData(
			r,
			"New Tag",
			"Protected Admin",
			"Create Tag",
			"Add a reusable label that can be attached to projects from day one and reused later in API key access rules.",
		),
		Form: TagFormData{
			Action:      "/admin/tags",
			SubmitLabel: "Create tag",
		},
	}

	renderPage(w, h.renderer, "tag_form", data)
}

func (h *handler) adminTagCreate(w http.ResponseWriter, r *http.Request) {
	if err := r.ParseForm(); err != nil {
		http.Error(w, "invalid tag form", http.StatusBadRequest)
		return
	}

	form := TagFormData{
		Action:      "/admin/tags",
		SubmitLabel: "Create tag",
		Name:        r.FormValue("name"),
		Slug:        r.FormValue("slug"),
		Description: r.FormValue("description"),
	}

	params, validationError := tagFormInput(form.Name, form.Slug, form.Description)
	form.Name = params.Name
	form.Slug = params.Slug
	form.Description = params.Description
	if validationError != "" {
		form.Error = validationError
		h.renderTagFormPage(w, r, http.StatusBadRequest, form)
		return
	}

	tag, err := h.store.Tags.Create(r.Context(), params)
	if err != nil {
		if errors.Is(err, db.ErrConflict) {
			form.Error = "A tag with that slug already exists."
			h.renderTagFormPage(w, r, http.StatusConflict, form)
			return
		}

		http.Error(w, "tag creation failed", http.StatusInternalServerError)
		return
	}

	http.Redirect(w, r, fmt.Sprintf("/admin/tags/%d?status=tag-created", tag.ID), http.StatusSeeOther)
}

func (h *handler) adminTagDetail(w http.ResponseWriter, r *http.Request) {
	tagID, err := routeID(r, "tagID")
	if err != nil {
		http.NotFound(w, r)
		return
	}

	data, err := h.tagDetailPageData(r, tagID, TagFormData{})
	if err != nil {
		h.renderTagDetailError(w, r, err)
		return
	}

	data.Flash = tagDetailFlash(r.URL.Query().Get("status"), r.URL.Query().Get("error"))
	renderPage(w, h.renderer, "tag_form", *data)
}

func (h *handler) adminTagUpdate(w http.ResponseWriter, r *http.Request) {
	tagID, err := routeID(r, "tagID")
	if err != nil {
		http.NotFound(w, r)
		return
	}

	if err := r.ParseForm(); err != nil {
		http.Error(w, "invalid tag form", http.StatusBadRequest)
		return
	}

	form := TagFormData{
		Action:      fmt.Sprintf("/admin/tags/%d", tagID),
		SubmitLabel: "Save tag",
		Name:        r.FormValue("name"),
		Slug:        r.FormValue("slug"),
		Description: r.FormValue("description"),
	}

	params, validationError := tagFormInput(form.Name, form.Slug, form.Description)
	form.Name = params.Name
	form.Slug = params.Slug
	form.Description = params.Description
	if validationError != "" {
		form.Error = validationError
		h.renderTagDetailPage(w, r, tagID, http.StatusBadRequest, form)
		return
	}

	_, err = h.store.Tags.Update(r.Context(), tagID, db.UpdateTagParams(params))
	if err != nil {
		switch {
		case errors.Is(err, db.ErrNotFound):
			http.NotFound(w, r)
		case errors.Is(err, db.ErrConflict):
			form.Error = "A tag with that slug already exists."
			h.renderTagDetailPage(w, r, tagID, http.StatusConflict, form)
		default:
			http.Error(w, "tag update failed", http.StatusInternalServerError)
		}
		return
	}

	http.Redirect(w, r, fmt.Sprintf("/admin/tags/%d?status=tag-updated", tagID), http.StatusSeeOther)
}

func (h *handler) adminTagDelete(w http.ResponseWriter, r *http.Request) {
	tagID, err := routeID(r, "tagID")
	if err != nil {
		http.NotFound(w, r)
		return
	}

	err = h.store.Tags.Delete(r.Context(), tagID)
	if err != nil {
		switch {
		case errors.Is(err, db.ErrNotFound):
			http.NotFound(w, r)
		case errors.Is(err, db.ErrConflict):
			http.Redirect(w, r, fmt.Sprintf("/admin/tags/%d?error=tag-in-use", tagID), http.StatusSeeOther)
		default:
			http.Error(w, "tag delete failed", http.StatusInternalServerError)
		}
		return
	}

	http.Redirect(w, r, "/admin/tags?status=tag-deleted", http.StatusSeeOther)
}

func (h *handler) renderTagFormPage(w http.ResponseWriter, r *http.Request, status int, form TagFormData) {
	data := TagFormPageData{
		PageData: h.basePageData(
			r,
			"New Tag",
			"Protected Admin",
			"Create Tag",
			"Add a reusable label that can be attached to projects from day one and reused later in API key access rules.",
		),
		Form: form,
	}

	renderPageStatus(w, h.renderer, "tag_form", status, data)
}

func (h *handler) renderTagDetailPage(w http.ResponseWriter, r *http.Request, tagID int64, status int, form TagFormData) {
	data, err := h.tagDetailPageData(r, tagID, form)
	if err != nil {
		h.renderTagDetailError(w, r, err)
		return
	}

	renderPageStatus(w, h.renderer, "tag_form", status, *data)
}

func (h *handler) renderTagDetailError(w http.ResponseWriter, r *http.Request, err error) {
	if errors.Is(err, db.ErrNotFound) {
		http.NotFound(w, r)
		return
	}

	http.Error(w, "tag page failed", http.StatusInternalServerError)
}

func (h *handler) tagDetailPageData(r *http.Request, tagID int64, form TagFormData) (*TagFormPageData, error) {
	tag, err := h.store.Tags.GetByID(r.Context(), tagID)
	if err != nil {
		return nil, err
	}

	projects, err := h.store.Tags.ListProjects(r.Context(), tagID)
	if err != nil {
		return nil, err
	}

	if form.Action == "" {
		form = TagFormData{
			Action:      fmt.Sprintf("/admin/tags/%d", tagID),
			SubmitLabel: "Save tag",
			Name:        tag.Name,
			Slug:        tag.Slug,
			Description: tag.Description,
			Error:       form.Error,
		}
	}
	form.DeleteAction = fmt.Sprintf("/admin/tags/%d/delete", tagID)
	form.CanDelete = len(projects) == 0

	data := &TagFormPageData{
		PageData: h.basePageData(
			r,
			tag.Name,
			"Protected Admin",
			tag.Name,
			"Edit this tag and review which projects already use it before preparing future access rules.",
		),
		Form:     form,
		Tag:      tag,
		Projects: projects,
	}

	return data, nil
}

func tagListFlash(code string) *FlashMessage {
	switch code {
	case "tag-deleted":
		return &FlashMessage{Kind: "success", Message: "Tag deleted."}
	default:
		return nil
	}
}

func tagDetailFlash(statusCode, errorCode string) *FlashMessage {
	switch statusCode {
	case "tag-created":
		return &FlashMessage{Kind: "success", Message: "Tag created. You can now attach it to projects."}
	case "tag-updated":
		return &FlashMessage{Kind: "success", Message: "Tag details updated."}
	}

	switch errorCode {
	case "tag-in-use":
		return &FlashMessage{Kind: "error", Message: "This tag is still attached to one or more projects, so it cannot be deleted yet."}
	default:
		return nil
	}
}
