package anime

import (
	"context"
	"fmt"
	"mal/internal/db"
	"mal/internal/domain"
	"mal/internal/server"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
)

type commandPaletteItem struct {
	ID       string `json:"id"`
	Type     string `json:"type"`
	Label    string `json:"label"`
	Subtitle string `json:"subtitle"`
	Href     string `json:"href"`
	Image    string `json:"image,omitempty"`
	Icon     string `json:"icon,omitempty"`
}

func (h *AnimeHandler) HandleCommandPalette(c *gin.Context) {
	user := server.CurrentUser(c)
	if user == nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
		return
	}

	query := strings.TrimSpace(c.Query("q"))
	items := make([]commandPaletteItem, 0, 12)

	if query != "" {
		items = append(items, commandPaletteItem{
			ID:       "search:" + strings.ToLower(query),
			Type:     "search",
			Label:    fmt.Sprintf("Search anime for %q", query),
			Subtitle: "Browse",
			Href:     "/browse?q=" + url.QueryEscape(query),
			Icon:     "search",
		})

		if len(query) >= 2 {
			items = append(items, h.commandPaletteAnimeResults(c, query)...)
		}

		items = append(items, h.commandPaletteNavigationItems(query)...)
		items = append(items, h.commandPaletteContinueItems(c, user.ID, query)...)
		items = append(items, h.commandPalettePersonalItems(c, user.ID, query)...)
		c.JSON(http.StatusOK, items)
		return
	}

	items = append(items, h.commandPaletteContinueItems(c, user.ID, query)...)
	items = append(items, h.commandPaletteNavigationItems(query)...)
	items = append(items, h.commandPalettePersonalItems(c, user.ID, query)...)
	c.JSON(http.StatusOK, items)
}

func (h *AnimeHandler) commandPaletteNavigationItems(query string) []commandPaletteItem {
	all := []commandPaletteItem{
		{ID: "nav:discover", Type: "navigation", Label: "Go to Discover", Subtitle: "Navigation", Href: "/discover", Icon: "compass"},
		{ID: "nav:watchlist", Type: "navigation", Label: "Go to Watchlist", Subtitle: "Navigation", Href: "/watchlist", Icon: "bookmark"},
		{ID: "nav:popular", Type: "navigation", Label: "Browse popular", Subtitle: "Browse", Href: "/browse?order_by=popularity&sort=desc", Icon: "trending"},
		{ID: "nav:airing", Type: "navigation", Label: "Currently airing", Subtitle: "Browse", Href: "/browse?status=airing&order_by=popularity&sort=desc", Icon: "play"},
	}
	if query == "" {
		return all
	}

	filtered := make([]commandPaletteItem, 0, len(all))
	for _, item := range all {
		if commandPaletteMatches(query, item.Label, item.Subtitle) {
			filtered = append(filtered, item)
		}
	}
	return filtered
}

func (h *AnimeHandler) commandPaletteAnimeResults(c *gin.Context, query string) []commandPaletteItem {
	searchCtx, cancel := context.WithTimeout(c.Request.Context(), 800*time.Millisecond)
	defer cancel()

	res, err := h.svc.SearchAdvanced(searchCtx, query, "", "", "", "", nil, 0, true, 1, 5)
	if err != nil {
		return nil
	}

	animes := wrapAnimes(res.Animes)
	items := make([]commandPaletteItem, 0, len(animes))
	for _, anime := range animes {
		items = append(items, commandPaletteItem{
			ID:       fmt.Sprintf("anime:%d", anime.MalID),
			Type:     "anime",
			Label:    anime.DisplayTitle(),
			Subtitle: strings.TrimSpace("Anime " + anime.Type),
			Href:     fmt.Sprintf("/anime/%d", anime.MalID),
			Image:    anime.ImageURL(),
		})
	}
	return items
}

func (h *AnimeHandler) commandPalettePersonalItems(c *gin.Context, userID string, query string) []commandPaletteItem {
	items := make([]commandPaletteItem, 0, 5)

	watchlist, err := h.watchlistSvc.GetCommandPaletteWatchlist(c.Request.Context(), userID, query, 5)
	if err != nil {
		return items
	}

	for _, entry := range watchlist {
		title := watchlistTitle(entry)
		items = append(items, commandPaletteItem{
			ID:       fmt.Sprintf("watchlist:%d", entry.AnimeID),
			Type:     "watchlist",
			Label:    title,
			Subtitle: watchlistStatusLabel(entry.Status),
			Href:     fmt.Sprintf("/anime/%d", entry.AnimeID),
			Image:    entry.ImageUrl,
		})
		if len(items) >= 5 {
			return items
		}
	}

	return items
}

func (h *AnimeHandler) commandPaletteContinueItems(c *gin.Context, userID string, query string) []commandPaletteItem {
	items := make([]commandPaletteItem, 0, 5)

	rows, err := h.watchlistSvc.GetCommandPaletteContinueWatching(c.Request.Context(), userID, query, 5)
	if err != nil {
		return items
	}

	for _, row := range rows {
		title := continueWatchingTitle(row)
		episode := ""
		href := fmt.Sprintf("/anime/%d/watch", row.AnimeID)
		if row.CurrentEpisode.Valid {
			episode = fmt.Sprintf(" episode %d", row.CurrentEpisode.Int64)
			href = fmt.Sprintf("%s?ep=%d", href, row.CurrentEpisode.Int64)
		}
		items = append(items, commandPaletteItem{
			ID:       fmt.Sprintf("continue:%d", row.AnimeID),
			Type:     "continue",
			Label:    "Continue watching " + title,
			Subtitle: "Resume" + episode,
			Href:     href,
			Image:    row.ImageUrl,
		})
		if len(items) >= 5 {
			return items
		}
	}

	return items
}

func commandPaletteMatches(query string, values ...string) bool {
	needle := strings.ToLower(strings.TrimSpace(query))
	for _, value := range values {
		if strings.Contains(strings.ToLower(value), needle) {
			return true
		}
	}
	return false
}

func continueWatchingTitle(row db.GetContinueWatchingEntriesRow) string {
	return row.DisplayTitle()
}

func watchlistTitle(row domain.UserWatchListRow) string {
	return row.DisplayTitle()
}

func watchlistStatusLabel(status string) string {
	switch status {
	case "watching":
		return "Watching"
	case "plan_to_watch":
		return "Plan to Watch"
	default:
		return "Watchlist"
	}
}
