package handlers

import (
	"context"
	"encoding/json"
	"net/http"
	"time"

	"lookbook/internal/data/item"
	"lookbook/internal/data/media"
	"lookbook/internal/data/tag"
)

type itemResponse struct {
	ID                 string   `json:"id"`
	Title              *string  `json:"title,omitempty"`
	Description        *string  `json:"description,omitempty"`
	LinkURL            *string  `json:"linkUrl,omitempty"`
	ItemType           string   `json:"itemType"`
	EmbedHTML          *string  `json:"embedHtml,omitempty"`
	Tags               []string `json:"tags"`
	CreatedAt          string   `json:"createdAt"`
	MediaID            *int64   `json:"mediaId,omitempty"`
	ThumbnailID        *int64   `json:"thumbnailId,omitempty"`
	ThumbnailSourceURL *string  `json:"thumbnailSourceUrl,omitempty"`
	GalleryIDs         []int64  `json:"galleryIds,omitempty"` // Additional images (for multi-image tweets)
}

type createItemRequest struct {
	Title         *string  `json:"title"`
	Description   *string  `json:"description"`
	LinkURL       *string  `json:"linkUrl"`
	ItemType      string   `json:"itemType"` // 'image', 'video', 'quote', 'embed'
	EmbedProvider *string  `json:"embedProvider"`
	EmbedVideoID  *string  `json:"embedVideoId"`
	EmbedHTML     *string  `json:"embedHtml"`
	Tags          []string `json:"tags"`
}

type updateItemRequest struct {
	Title       *string  `json:"title"`
	Description *string  `json:"description"`
	LinkURL     *string  `json:"linkUrl"`
	Tags        []string `json:"tags"`
}

// HandleListItems handles GET /api/items
func HandleListItems(rc *RequestContext, w http.ResponseWriter, r *http.Request) error {
	ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
	defer cancel()

	tagFilter := r.URL.Query().Get("tag")

	var items []item.Row
	var err error
	if tagFilter != "" {
		items, err = item.QListByTag(ctx, rc.DB, tagFilter)
	} else {
		items, err = item.QList(ctx, rc.DB)
	}
	if err != nil {
		return err
	}

	response := make([]itemResponse, 0, len(items))
	for _, it := range items {
		resp, err := buildItemResponse(ctx, rc, it)
		if err != nil {
			return err
		}
		response = append(response, resp)
	}

	return writeJSON(w, http.StatusOK, response)
}

// HandleGetItem handles GET /api/items/{id}
func HandleGetItem(rc *RequestContext, w http.ResponseWriter, r *http.Request) error {
	pubID := r.PathValue("id")
	if pubID == "" {
		return writeJSON(w, http.StatusBadRequest, map[string]string{"error": "missing id"})
	}

	ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
	defer cancel()

	it, err := item.QFindByPubID(ctx, rc.DB, pubID)
	if err != nil {
		return err
	}
	if it == nil {
		return writeJSON(w, http.StatusNotFound, map[string]string{"error": "not found"})
	}

	resp, err := buildItemResponse(ctx, rc, *it)
	if err != nil {
		return err
	}

	return writeJSON(w, http.StatusOK, resp)
}

// HandleCreateItem handles POST /api/items
func HandleCreateItem(rc *RequestContext, w http.ResponseWriter, r *http.Request) error {
	if !rc.RequireAdmin(w) {
		return nil
	}

	var req createItemRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		return writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request"})
	}

	if req.ItemType == "" {
		return writeJSON(w, http.StatusBadRequest, map[string]string{"error": "itemType required"})
	}

	ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
	defer cancel()

	it, err := item.QCreate(ctx, rc.DB, item.CreateParams{
		Title:         item.Nullable(req.Title),
		Description:   item.Nullable(req.Description),
		LinkURL:       item.Nullable(req.LinkURL),
		ItemType:      req.ItemType,
		EmbedProvider: item.Nullable(req.EmbedProvider),
		EmbedVideoID:  item.Nullable(req.EmbedVideoID),
		EmbedHTML:     item.Nullable(req.EmbedHTML),
	})
	if err != nil {
		return err
	}

	// Set tags
	if len(req.Tags) > 0 {
		if err := tag.QSetTagsForItem(ctx, rc.DB, it.ID, req.Tags); err != nil {
			return err
		}
	}

	resp, err := buildItemResponse(ctx, rc, it)
	if err != nil {
		return err
	}

	return writeJSON(w, http.StatusCreated, resp)
}

// HandleUpdateItem handles PUT /api/items/{id}
func HandleUpdateItem(rc *RequestContext, w http.ResponseWriter, r *http.Request) error {
	if !rc.RequireAdmin(w) {
		return nil
	}

	pubID := r.PathValue("id")
	if pubID == "" {
		return writeJSON(w, http.StatusBadRequest, map[string]string{"error": "missing id"})
	}

	var req updateItemRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		return writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request"})
	}

	ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
	defer cancel()

	it, err := item.QFindByPubID(ctx, rc.DB, pubID)
	if err != nil {
		return err
	}
	if it == nil {
		return writeJSON(w, http.StatusNotFound, map[string]string{"error": "not found"})
	}

	if err := item.QUpdate(ctx, rc.DB, it.ID, item.UpdateParams{
		Title:       item.Nullable(req.Title),
		Description: item.Nullable(req.Description),
		LinkURL:     item.Nullable(req.LinkURL),
	}); err != nil {
		return err
	}

	// Update tags
	if err := tag.QSetTagsForItem(ctx, rc.DB, it.ID, req.Tags); err != nil {
		return err
	}

	// Refetch to return updated item
	it, err = item.QFindByID(ctx, rc.DB, it.ID)
	if err != nil {
		return err
	}

	resp, err := buildItemResponse(ctx, rc, *it)
	if err != nil {
		return err
	}

	return writeJSON(w, http.StatusOK, resp)
}

// HandleDeleteItem handles DELETE /api/items/{id}
func HandleDeleteItem(rc *RequestContext, w http.ResponseWriter, r *http.Request) error {
	if !rc.RequireAdmin(w) {
		return nil
	}

	pubID := r.PathValue("id")
	if pubID == "" {
		return writeJSON(w, http.StatusBadRequest, map[string]string{"error": "missing id"})
	}

	ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
	defer cancel()

	it, err := item.QFindByPubID(ctx, rc.DB, pubID)
	if err != nil {
		return err
	}
	if it == nil {
		return writeJSON(w, http.StatusNotFound, map[string]string{"error": "not found"})
	}

	if err := item.QSoftDelete(ctx, rc.DB, it.ID); err != nil {
		return err
	}

	return writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
}

func buildItemResponse(ctx context.Context, rc *RequestContext, it item.Row) (itemResponse, error) {
	tags, err := tag.QTagsForItem(ctx, rc.DB, it.ID)
	if err != nil {
		return itemResponse{}, err
	}

	tagNames := make([]string, len(tags))
	for i, t := range tags {
		tagNames[i] = t.Name
	}

	resp := itemResponse{
		ID:          it.PubID,
		Title:       item.Ptr(it.Title),
		Description: item.Ptr(it.Description),
		LinkURL:     item.Ptr(it.LinkURL),
		ItemType:    it.ItemType,
		EmbedHTML:   item.Ptr(it.EmbedHTML),
		Tags:        tagNames,
		CreatedAt:   it.CreatedAt.Format(time.RFC3339),
	}

	// Get media IDs
	// Media is ordered by ID, so first "image" is the thumbnail, rest are gallery
	mediaList, err := media.QFindByItemID(ctx, rc.DB, it.ID)
	if err != nil {
		return itemResponse{}, err
	}
	firstImage := true
	for _, m := range mediaList {
		if m.MediaType == "original" {
			resp.MediaID = &m.ID
		} else if m.MediaType == "image" {
			if firstImage {
				resp.ThumbnailID = &m.ID
				resp.ThumbnailSourceURL = m.SourceURL
				firstImage = false
			} else {
				resp.GalleryIDs = append(resp.GalleryIDs, m.ID)
			}
		}
	}

	return resp, nil
}
