package handlers

import (
	"errors"
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/google/uuid"

	"{{GO_MODULE}}/pkg/app"
	"{{GO_MODULE}}/pkg/httperror"
	"{{GO_MODULE}}/pkg/httpresponse"
	"{{GO_MODULE}}/pkg/logging"
	"{{GO_MODULE}}/services/{{COMPONENT_NAME}}/internal/domain"
	"{{GO_MODULE}}/services/{{COMPONENT_NAME}}/internal/service"
)

// Example handles HTTP requests for example resources.
type Example struct {
	svc    *service.ExampleService
	logger *logging.Logger
}

// NewExample creates a new Example handler with injected dependencies.
func NewExample(svc *service.ExampleService, logger *logging.Logger) *Example {
	return &Example{
		svc:    svc,
		logger: logger.WithComponent("ExampleHandler"),
	}
}

// CreateRequest is the request body for creating an example.
type CreateRequest struct {
	Name        string `json:"name" validate:"required,min=1,max=100"`
	Description string `json:"description" validate:"max=500"`
}

// UpdateRequest is the request body for updating an example.
type UpdateRequest struct {
	Name        string `json:"name" validate:"required,min=1,max=100"`
	Description string `json:"description" validate:"max=500"`
}

// ExampleResponse is the response for an example resource.
type ExampleResponse struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
	CreatedAt   string `json:"created_at"`
	UpdatedAt   string `json:"updated_at"`
}

// toResponse converts a domain example to an API response.
func toResponse(e *domain.Example) ExampleResponse {
	return ExampleResponse{
		ID:          e.ID.String(),
		Name:        e.Name,
		Description: e.Description,
		CreatedAt:   e.CreatedAt.Format("2006-01-02T15:04:05Z"),
		UpdatedAt:   e.UpdatedAt.Format("2006-01-02T15:04:05Z"),
	}
}

// List returns all examples.
func (h *Example) List(w http.ResponseWriter, r *http.Request) error {
	examples, err := h.svc.List(r.Context())
	if err != nil {
		return err
	}

	result := make([]ExampleResponse, len(examples))
	for i, e := range examples {
		result[i] = toResponse(&e)
	}

	httpresponse.OK(w, r, result)
	return nil
}

// Get returns an example by ID.
func (h *Example) Get(w http.ResponseWriter, r *http.Request) error {
	id := chi.URLParam(r, "id")

	// Validate UUID format
	if _, err := uuid.Parse(id); err != nil {
		return httperror.BadRequest("invalid id format")
	}

	example, err := h.svc.Get(r.Context(), domain.ExampleID(id))
	if err != nil {
		return mapDomainError(err)
	}

	httpresponse.OK(w, r, toResponse(example))
	return nil
}

// Create creates a new example.
func (h *Example) Create(w http.ResponseWriter, r *http.Request) error {
	var req CreateRequest
	if err := app.BindAndValidate(r, &req); err != nil {
		return err
	}

	example, err := h.svc.Create(r.Context(), service.CreateInput{
		Name:        req.Name,
		Description: req.Description,
	})
	if err != nil {
		return mapDomainError(err)
	}

	httpresponse.Created(w, r, toResponse(example))
	return nil
}

// Update updates an existing example.
func (h *Example) Update(w http.ResponseWriter, r *http.Request) error {
	id := chi.URLParam(r, "id")

	if _, err := uuid.Parse(id); err != nil {
		return httperror.BadRequest("invalid id format")
	}

	var req UpdateRequest
	if err := app.BindAndValidate(r, &req); err != nil {
		return err
	}

	example, err := h.svc.Update(r.Context(), domain.ExampleID(id), service.UpdateInput{
		Name:        req.Name,
		Description: req.Description,
	})
	if err != nil {
		return mapDomainError(err)
	}

	httpresponse.OK(w, r, toResponse(example))
	return nil
}

// Delete removes an example by ID.
func (h *Example) Delete(w http.ResponseWriter, r *http.Request) error {
	id := chi.URLParam(r, "id")

	if _, err := uuid.Parse(id); err != nil {
		return httperror.BadRequest("invalid id format")
	}

	if err := h.svc.Delete(r.Context(), domain.ExampleID(id)); err != nil {
		return mapDomainError(err)
	}

	httpresponse.NoContent(w)
	return nil
}

// mapDomainError converts domain errors to HTTP errors.
func mapDomainError(err error) error {
	switch {
	case errors.Is(err, domain.ErrExampleNotFound):
		return httperror.NotFound("example not found")
	case errors.Is(err, domain.ErrDuplicateExample):
		return httperror.Conflict("example with this name already exists")
	case errors.Is(err, domain.ErrInvalidExampleName):
		return httperror.BadRequest("invalid example name")
	default:
		return err
	}
}
