// Package handlers implements HTTP request handlers for the API.
package handlers

import (
	"encoding/json"
	"log"
	"net/http"
	"regexp"
	"strings"

	"github.com/kjannette/koin-ping/backend/internal/domain"
	"github.com/kjannette/koin-ping/backend/internal/middleware"
	"github.com/kjannette/koin-ping/backend/internal/models"
)

var ethAddressRe = regexp.MustCompile(`^0x[a-fA-F0-9]{40}$`)

type AddressHandler struct {
	addresses *models.AddressModel
}

func NewAddressHandler(addresses *models.AddressModel) *AddressHandler {
	return &AddressHandler{addresses: addresses}
}

func (h *AddressHandler) Create(w http.ResponseWriter, r *http.Request) {
	userID := middleware.GetUserID(r.Context())

	var body struct {
		Address string  `json:"address"`
		Label   *string `json:"label"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		log.Printf("Failed to decode address request body: %v", err)
		writeError(w, http.StatusBadRequest, "VALIDATION_ERROR", "Invalid request body")

		return
	}

	if body.Address == "" {
		writeError(w, http.StatusBadRequest, "VALIDATION_ERROR", "Address is required")

		return
	}

	if !ethAddressRe.MatchString(body.Address) {
		writeError(w, http.StatusBadRequest, "VALIDATION_ERROR", "Invalid Ethereum address format")

		return
	}

	log.Printf("User %s creating address: %s", userID, body.Address)

	addr, err := h.addresses.Create(r.Context(), userID, body.Address, body.Label)
	if err != nil {
		if strings.Contains(err.Error(), "23505") || strings.Contains(err.Error(), "unique") {
			writeError(w, http.StatusBadRequest, "VALIDATION_ERROR", "You are already tracking this address")

			return
		}
		log.Printf("Error creating address: %v", err)
		writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "Failed to create address")

		return
	}

	log.Printf("Address created with ID: %d", addr.ID)
	writeJSON(w, http.StatusCreated, addr)
}

// List handles GET requests to list all tracked addresses for the current user.
func (h *AddressHandler) List(w http.ResponseWriter, r *http.Request) {
	userID := middleware.GetUserID(r.Context())
	log.Printf("User %s listing addresses", userID)

	addresses, err := h.addresses.ListByUser(r.Context(), userID)
	if err != nil {
		log.Printf("Error listing addresses: %v", err)
		writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "Failed to list addresses")

		return
	}

	if addresses == nil {
		addresses = []domain.Address{}
	}

	log.Printf("Found %d addresses for user", len(addresses))
	writeJSON(w, http.StatusOK, addresses)
}

//  handles PATCH requests to update an address label.
func (h *AddressHandler) UpdateLabel(w http.ResponseWriter, r *http.Request) {
	userID := middleware.GetUserID(r.Context())
	addressID, ok := parseIntParam(r.PathValue("addressId"))
	if !ok {
		writeError(w, http.StatusBadRequest, "VALIDATION_ERROR", "Invalid address ID")

		return
	}

	var body struct {
		Label *string `json:"label"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeError(w, http.StatusBadRequest, "VALIDATION_ERROR", "Invalid request body")

		return
	}

	log.Printf("User %s updating label for address ID: %d", userID, addressID)

	addr, err := h.addresses.UpdateLabel(r.Context(), addressID, userID, body.Label)
	if err != nil {
		log.Printf("Error updating address label: %v", err)
		writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "Failed to update address")

		return
	}

	if addr == nil {
		writeError(w, http.StatusNotFound, "NOT_FOUND", "Address not found")

		return
	}

	writeJSON(w, http.StatusOK, addr)
}

// Remove handles DELETE requests to remove a tracked address.
func (h *AddressHandler) Remove(w http.ResponseWriter, r *http.Request) {
	userID := middleware.GetUserID(r.Context())
	addressID, ok := parseIntParam(r.PathValue("addressId"))
	if !ok {
		writeError(w, http.StatusBadRequest, "VALIDATION_ERROR", "Invalid address ID")

		return
	}

	log.Printf("User %s deleting address ID: %d", userID, addressID)

	deleted, err := h.addresses.Remove(r.Context(), addressID, userID)
	if err != nil {
		log.Printf("Error deleting address: %v", err)
		writeError(w, http.StatusInternalServerError, "INTERNAL_ERROR", "Failed to delete address")

		return
	}

	if !deleted {
		log.Printf("Address %d not found or not owned by user", addressID)
		writeError(w, http.StatusNotFound, "NOT_FOUND", "Address not found")

		return
	}

	log.Printf("Address %d deleted", addressID)
	w.WriteHeader(http.StatusNoContent)
}
