package handlers

import (
	"errors"
	"net/http"
	"strconv"
	"strings"

	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgtype"

	"wherewoof/frontend/internal/auth"
	"wherewoof/frontend/internal/db"
)

const maxTagsPerAccount = 20

type accountData struct {
	Tags     []db.Tag
	AddError string
}

func ownerID(uid int64) pgtype.Int8 {
	return pgtype.Int8{Int64: uid, Valid: true}
}

func (a *App) Account(w http.ResponseWriter, r *http.Request) {
	uid, _ := auth.GetUserID(r)
	tags, err := a.Queries.ListTagsByOwner(r.Context(), ownerID(uid))
	if err != nil {
		http.Error(w, "internal error", http.StatusInternalServerError)
		return
	}
	a.render(w, r, "account", "My Tags", accountData{Tags: tags}, "")
}

// AddTag binds a tag code to the current account (HTMX: returns account-panel).
func (a *App) AddTag(w http.ResponseWriter, r *http.Request) {
	uid, _ := auth.GetUserID(r)
	code := strings.ToUpper(strings.TrimSpace(r.FormValue("tag_code")))
	oid := ownerID(uid)
	data := accountData{}

	if code == "" {
		data.AddError = "Enter a tag code."
	} else {
		cnt, err := a.Queries.CountTagsByOwner(r.Context(), oid)
		if err != nil {
			http.Error(w, "internal error", http.StatusInternalServerError)
			return
		}
		if cnt >= maxTagsPerAccount {
			data.AddError = "Limit reached: each account can hold 20 tags."
		} else {
			_, err := a.Queries.BindTag(r.Context(), db.BindTagParams{OwnerID: oid, TagCode: code})
			if err != nil {
				if errors.Is(err, pgx.ErrNoRows) {
					data.AddError = "Tag not found, or already claimed by another account."
				} else {
					http.Error(w, "internal error", http.StatusInternalServerError)
					return
				}
			}
		}
	}

	tags, err := a.Queries.ListTagsByOwner(r.Context(), oid)
	if err != nil {
		http.Error(w, "internal error", http.StatusInternalServerError)
		return
	}
	data.Tags = tags
	a.renderPartial(w, "account", "account-panel", data)
}

func (a *App) EditTagPage(w http.ResponseWriter, r *http.Request) {
	uid, _ := auth.GetUserID(r)
	tag, ok := a.loadOwnedTag(w, r, uid)
	if !ok {
		return
	}
	if tag.Status == "suspended" {
		a.render(w, r, "edit", "Edit tag", tag, "This tag is suspended and cannot be edited.")
		return
	}
	a.render(w, r, "edit", "Edit tag", tag, "")
}

func (a *App) EditTag(w http.ResponseWriter, r *http.Request) {
	uid, _ := auth.GetUserID(r)
	tag, ok := a.loadOwnedTag(w, r, uid)
	if !ok {
		return
	}
	if tag.Status == "suspended" {
		a.render(w, r, "edit", "Edit tag", tag, "This tag is suspended and cannot be edited.")
		return
	}

	params := db.UpdateTagDetailsParams{
		ID:          tag.ID,
		ItemType:    textOrNil(strings.TrimSpace(r.FormValue("item_type"))),
		Description: textOrNil(strings.TrimSpace(r.FormValue("description"))),
		PhotoUrl:    textOrNil(strings.TrimSpace(r.FormValue("photo_url"))),
		Phone:       textOrNil(strings.TrimSpace(r.FormValue("phone"))),
		Address:     textOrNil(strings.TrimSpace(r.FormValue("address"))),
		Notes:       textOrNil(strings.TrimSpace(r.FormValue("notes"))),
		SmsEnabled:  r.FormValue("sms_enabled") == "on",
	}
	if _, err := a.Queries.UpdateTagDetails(r.Context(), params); err != nil {
		http.Error(w, "internal error", http.StatusInternalServerError)
		return
	}
	http.Redirect(w, r, "/account", http.StatusSeeOther)
}

// DeleteTag reverts a tag to unset so it can be re-bound (HTMX: account-panel).
func (a *App) DeleteTag(w http.ResponseWriter, r *http.Request) {
	uid, _ := auth.GetUserID(r)
	tag, ok := a.loadOwnedTag(w, r, uid)
	if !ok {
		return
	}
	if _, err := a.Queries.ClearTagOwner(r.Context(), tag.ID); err != nil {
		http.Error(w, "internal error", http.StatusInternalServerError)
		return
	}
	tags, err := a.Queries.ListTagsByOwner(r.Context(), ownerID(uid))
	if err != nil {
		http.Error(w, "internal error", http.StatusInternalServerError)
		return
	}
	a.renderPartial(w, "account", "account-panel", accountData{Tags: tags})
}

// loadOwnedTag fetches a tag by path id and verifies it belongs to uid.
func (a *App) loadOwnedTag(w http.ResponseWriter, r *http.Request, uid int64) (db.Tag, bool) {
	id, err := strconv.ParseInt(r.PathValue("id"), 10, 64)
	if err != nil {
		http.NotFound(w, r)
		return db.Tag{}, false
	}
	tag, err := a.Queries.GetTagByID(r.Context(), id)
	if err != nil || !tag.OwnerID.Valid || tag.OwnerID.Int64 != uid {
		http.NotFound(w, r)
		return db.Tag{}, false
	}
	return tag, true
}

func textOrNil(s string) pgtype.Text {
	return pgtype.Text{String: s, Valid: s != ""}
}
