package handlers

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

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

	"git.threesix.ai/jordan/persona-community-5/pkg/app"
	"git.threesix.ai/jordan/persona-community-5/pkg/httperror"
	"git.threesix.ai/jordan/persona-community-5/pkg/httpresponse"
	"git.threesix.ai/jordan/persona-community-5/pkg/logging"
	"git.threesix.ai/jordan/persona-community-5/services/persona-api/internal/domain"
	"git.threesix.ai/jordan/persona-community-5/services/persona-api/internal/service"
)

// Persona handles HTTP requests for persona CRUD operations.
type Persona struct {
	svc    *service.PersonaService
	logger *logging.Logger
}

// NewPersona creates a new Persona handler with injected dependencies.
func NewPersona(svc *service.PersonaService, logger *logging.Logger) *Persona {
	return &Persona{
		svc:    svc,
		logger: logger.WithComponent("PersonaHandler"),
	}
}

// CreatePersonaRequest is the request body for creating a persona.
type CreatePersonaRequest struct {
	Description string `json:"description" validate:"required,min=3,max=1000"`
	Gender      string `json:"gender" validate:"required,oneof=woman man non_binary"`
	CustomName  string `json:"custom_name"`
}

// PersonaResponse is the API representation of a persona.
type PersonaResponse struct {
	ID          string   `json:"id"`
	Name        string   `json:"name"`
	Handle      string   `json:"handle"`
	Gender      string   `json:"gender"`
	Description string   `json:"description"`
	Tags        []string `json:"tags"`
	SpecJSON    any      `json:"spec_json,omitempty"`
	AnchorURL   string   `json:"anchor_url,omitempty"`
	AvatarURL   string   `json:"avatar_url,omitempty"`
	BannerURL   string   `json:"banner_url,omitempty"`
	ImageURLs   []string `json:"image_urls"`
	VideoURLs   []string `json:"video_urls"`
	Status      string   `json:"status"`
	CreatedAt   string   `json:"created_at"`
}

func toPersonaResponse(p *domain.Persona) PersonaResponse {
	resp := PersonaResponse{
		ID:          p.ID.String(),
		Name:        p.Name,
		Handle:      p.Handle,
		Gender:      p.Gender,
		Description: p.Description,
		Tags:        p.Tags,
		AnchorURL:   p.AnchorURL,
		AvatarURL:   p.AvatarURL,
		BannerURL:   p.BannerURL,
		ImageURLs:   p.ImageURLs,
		VideoURLs:   p.VideoURLs,
		Status:      string(p.Status),
		CreatedAt:   p.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
	}
	if p.SpecJSON != nil {
		resp.SpecJSON = p.SpecJSON
	}
	return resp
}

// Create creates a new persona and enqueues a generate_spec job.
// Returns 202 Accepted with the created persona.
func (h *Persona) Create(w http.ResponseWriter, r *http.Request) error {
	var req CreatePersonaRequest
	if err := app.BindAndValidate(r, &req); err != nil {
		return err
	}

	persona, err := h.svc.Create(r.Context(), req.Description, req.Gender, req.CustomName)
	if err != nil {
		return mapPersonaDomainError(err)
	}

	httpresponse.Accepted(w, r, toPersonaResponse(persona))
	return nil
}

// GetByID returns a single persona by ID.
func (h *Persona) GetByID(w http.ResponseWriter, r *http.Request) error {
	id := chi.URLParam(r, "id")
	if id == "" {
		return httperror.BadRequest("persona ID is required")
	}

	persona, err := h.svc.GetByID(r.Context(), domain.PersonaID(id))
	if err != nil {
		return mapPersonaDomainError(err)
	}

	httpresponse.OK(w, r, toPersonaResponse(persona))
	return nil
}

// List returns a paginated list of personas.
func (h *Persona) List(w http.ResponseWriter, r *http.Request) error {
	limit := 20
	offset := 0

	if v := r.URL.Query().Get("limit"); v != "" {
		if parsed, err := strconv.Atoi(v); err == nil {
			limit = parsed
		}
	}
	if v := r.URL.Query().Get("offset"); v != "" {
		if parsed, err := strconv.Atoi(v); err == nil {
			offset = parsed
		}
	}

	personas, err := h.svc.List(r.Context(), limit, offset)
	if err != nil {
		return err
	}

	results := make([]PersonaResponse, len(personas))
	for i, p := range personas {
		results[i] = toPersonaResponse(p)
	}

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

func mapPersonaDomainError(err error) error {
	switch {
	case errors.Is(err, domain.ErrPersonaNotFound):
		return httperror.NotFound("persona not found")
	case errors.Is(err, domain.ErrDuplicateHandle):
		return httperror.Conflict("persona with this handle already exists")
	default:
		return err
	}
}
