package anime

import (
	"encoding/json"
	"log"
	"net/http"
	"strconv"

	"mal/internal/database"
	"mal/internal/jikan"
	"mal/internal/shared/middleware"
	"mal/internal/templates"
)

func deduplicateAnimes(animes []jikan.Anime) []jikan.Anime {
	seen := make(map[int]bool)
	var result []jikan.Anime
	for _, a := range animes {
		if !seen[a.MalID] {
			seen[a.MalID] = true
			result = append(result, a)
		}
	}
	return result
}

type Handler struct {
	svc *Service
}

func NewHandler(svc *Service) *Handler {
	return &Handler{svc: svc}
}

func (h *Handler) HandleCatalog(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" {
		http.NotFound(w, r)
		return
	}
	templates.Catalog().Render(r.Context(), w)
}

func (h *Handler) HandleSearch(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Vary", "HX-Request")

	query := r.URL.Query().Get("q")
	if query == "" {
		templates.Search("").Render(r.Context(), w)
		return
	}

	if r.Header.Get("HX-Request") == "true" {
		res, err := h.svc.Search(query, 1)
		if err != nil {
			log.Printf("search error: %v", err)
			http.Error(w, "Failed to search anime", http.StatusInternalServerError)
			return
		}
		templates.SearchResultsWrapper(query, res.Animes, 2, res.HasNextPage).Render(r.Context(), w)
		return
	}

	templates.Search(query).Render(r.Context(), w)
}

func (h *Handler) HandleAPISearch(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query().Get("q")
	pageStr := r.URL.Query().Get("page")
	page, _ := strconv.Atoi(pageStr)
	if page < 1 {
		page = 1
	}

	res, err := h.svc.Search(query, page)
	if err != nil {
		log.Printf("search pagination error: %v", err)
		http.Error(w, "Failed to fetch search page", http.StatusInternalServerError)
		return
	}

	res.Animes = deduplicateAnimes(res.Animes)

	templates.SearchItems(query, res.Animes, page+1, res.HasNextPage).Render(r.Context(), w)
}

func (h *Handler) HandleAPICatalog(w http.ResponseWriter, r *http.Request) {
	pageStr := r.URL.Query().Get("page")
	page, _ := strconv.Atoi(pageStr)
	if page < 1 {
		page = 1
	}

	res, err := h.svc.GetTopAnime(page)
	if err != nil {
		log.Printf("top anime error: %v", err)
		http.Error(w, "Failed to fetch top anime", http.StatusInternalServerError)
		return
	}

	res.Animes = deduplicateAnimes(res.Animes)

	templates.CatalogItems(res.Animes, page+1, res.HasNextPage).Render(r.Context(), w)
}

func (h *Handler) HandleAnimeDetails(w http.ResponseWriter, r *http.Request) {
	idStr := r.URL.Path[len("/anime/"):]
	id, err := strconv.Atoi(idStr)
	if err != nil || id <= 0 {
		http.NotFound(w, r)
		return
	}

	userID := ""
	if user, ok := r.Context().Value(middleware.UserContextKey).(*database.User); ok && user != nil {
		userID = user.ID
	}

	anime, currentStatus, err := h.svc.GetAnimeDetails(r.Context(), id, userID)
	if err != nil {
		log.Printf("anime fetch error for %d: %v", id, err)
		http.Error(w, "Failed to fetch anime details", http.StatusInternalServerError)
		return
	}

	templates.AnimeDetails(anime, currentStatus).Render(r.Context(), w)
}

func (h *Handler) HandleAPIAnimeRelations(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path[len("/api/anime/"):]
	idStr := ""
	for i, c := range path {
		if c == '/' {
			idStr = path[:i]
			break
		}
	}

	id, _ := strconv.Atoi(idStr)
	if id <= 0 {
		http.Error(w, "invalid id", http.StatusBadRequest)
		return
	}

	relations := h.svc.GetRelations(id)
	templates.AnimeRelationsList(relations).Render(r.Context(), w)
}

// HandleAPIAnime routes anime API requests
func (h *Handler) HandleAPIAnime(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path[len("/api/anime/"):]

	// Parse: {id}/relations or {id}/recommendations
	parts := splitPath(path)
	if len(parts) < 2 {
		http.Error(w, "invalid path", http.StatusBadRequest)
		return
	}

	id, err := strconv.Atoi(parts[0])
	if err != nil || id <= 0 {
		http.Error(w, "invalid id", http.StatusBadRequest)
		return
	}

	switch parts[1] {
	case "relations":
		relations := h.svc.GetRelations(id)
		templates.AnimeRelationsList(relations).Render(r.Context(), w)
	case "recommendations":
		recs, err := h.svc.GetRecommendations(id)
		if err != nil {
			log.Printf("recommendations error for %d: %v", id, err)
			http.Error(w, "Failed to fetch recommendations", http.StatusInternalServerError)
			return
		}
		if len(recs) > 10 {
			recs = recs[:10]
		}
		templates.AnimeRecommendations(recs).Render(r.Context(), w)
	default:
		http.Error(w, "not found", http.StatusNotFound)
	}
}

func splitPath(path string) []string {
	var parts []string
	var current string
	for _, c := range path {
		if c == '/' {
			if current != "" {
				parts = append(parts, current)
				current = ""
			}
		} else {
			current += string(c)
		}
	}
	if current != "" {
		parts = append(parts, current)
	}
	return parts
}

func (h *Handler) HandleQuickSearch(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query().Get("q")
	if query == "" {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		json.NewEncoder(w).Encode([]interface{}{})
		return
	}

	res, err := h.svc.Search(query, 1)
	if err != nil {
		log.Printf("quick search error: %v", err)
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	// Limit to 5 results
	results := res.Animes
	if len(results) > 5 {
		results = results[:5]
	}

	type SearchResult struct {
		ID    int    `json:"id"`
		Title string `json:"title"`
		Type  string `json:"type"`
		Image string `json:"image"`
	}

	output := make([]SearchResult, len(results))
	for i, anime := range results {
		output[i] = SearchResult{
			ID:    anime.MalID,
			Title: anime.DisplayTitle(),
			Type:  anime.Type,
			Image: anime.ImageURL(),
		}
	}

	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(output)
}

func (h *Handler) HandleDiscover(w http.ResponseWriter, r *http.Request) {
	templates.Discover().Render(r.Context(), w)
}

func (h *Handler) HandleAPIDiscoverAiring(w http.ResponseWriter, r *http.Request) {
	pageStr := r.URL.Query().Get("page")
	page, _ := strconv.Atoi(pageStr)
	if page < 1 {
		page = 1
	}

	res, err := h.svc.GetAiringAnime(page)
	if err != nil {
		log.Printf("airing anime error: %v", err)
		http.Error(w, "Failed to fetch airing anime", http.StatusInternalServerError)
		return
	}

	res.Animes = deduplicateAnimes(res.Animes)

	templates.DiscoverItems(res.Animes, "airing", page+1, res.HasNextPage).Render(r.Context(), w)
}

func (h *Handler) HandleAPIDiscoverUpcoming(w http.ResponseWriter, r *http.Request) {
	pageStr := r.URL.Query().Get("page")
	page, _ := strconv.Atoi(pageStr)
	if page < 1 {
		page = 1
	}

	res, err := h.svc.GetUpcomingAnime(page)
	if err != nil {
		log.Printf("upcoming anime error: %v", err)
		http.Error(w, "Failed to fetch upcoming anime", http.StatusInternalServerError)
		return
	}

	res.Animes = deduplicateAnimes(res.Animes)

	templates.DiscoverItems(res.Animes, "upcoming", page+1, res.HasNextPage).Render(r.Context(), w)
}

func (h *Handler) HandleSchedule(w http.ResponseWriter, r *http.Request) {
	templates.Schedule().Render(r.Context(), w)
}

func (h *Handler) HandleAPISchedule(w http.ResponseWriter, r *http.Request) {
	day := r.URL.Query().Get("day")
	if day == "" {
		day = "monday"
	}

	res, err := h.svc.GetSchedule(day)
	if err != nil {
		log.Printf("schedule error for %s: %v", day, err)
		http.Error(w, "Failed to fetch schedule", http.StatusInternalServerError)
		return
	}

	res.Animes = deduplicateAnimes(res.Animes)

	templates.ScheduleDay(day, res.Animes).Render(r.Context(), w)
}

func (h *Handler) HandleNotifications(w http.ResponseWriter, r *http.Request) {
	userID := ""
	if user, ok := r.Context().Value(middleware.UserContextKey).(*database.User); ok && user != nil {
		userID = user.ID
	}

	if userID == "" {
		http.Redirect(w, r, "/login", http.StatusSeeOther)
		return
	}

	watching, err := h.svc.GetWatchingAnime(r.Context(), userID)
	if err != nil {
		log.Printf("watching anime error: %v", err)
		http.Error(w, "Failed to fetch watching anime", http.StatusInternalServerError)
		return
	}

	templates.Notifications(watching).Render(r.Context(), w)
}
