package server

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"
)

// JellyfinXConfig holds the Jellyfin connection + the toggleable "X" library
// definition. Populated from CC_JELLYFIN_* env vars in main.go; an empty
// BaseURL or Token disables the feature (handlers return 503).
//
// The toggle adds/removes the X library as a Jellyfin VirtualFolder. When
// removed, Jellyfin purges those items from its database entirely — they
// vanish from views, search, and watch history (not merely hidden). The
// media files on disk are never touched; only Jellyfin's view of them flips.
type JellyfinXConfig struct {
	BaseURL string // e.g. https://host/user/jellyfin (no trailing slash needed)
	Token   string // Jellyfin API key (X-Emby-Token)
	Name    string // library name to add/remove, e.g. "X"
	Path    string // folder the library points at, e.g. /media/.../media-x
}

func (s *Server) jellyfinReady() bool {
	return s.jellyfinX != nil && s.jellyfinX.BaseURL != "" && s.jellyfinX.Token != ""
}

func (s *Server) jellyfinRequest(ctx context.Context, method, path string, body io.Reader) (*http.Response, error) {
	u := strings.TrimRight(s.jellyfinX.BaseURL, "/") + path
	req, err := http.NewRequestWithContext(ctx, method, u, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("X-Emby-Token", s.jellyfinX.Token)
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	return (&http.Client{Timeout: 25 * time.Second}).Do(req)
}

// xLibraryPresent reports whether the configured X library currently exists.
func (s *Server) xLibraryPresent(ctx context.Context) (bool, error) {
	resp, err := s.jellyfinRequest(ctx, http.MethodGet, "/Library/VirtualFolders", nil)
	if err != nil {
		return false, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return false, fmt.Errorf("jellyfin VirtualFolders: status %d", resp.StatusCode)
	}
	var folders []struct {
		Name string `json:"Name"`
	}
	if err := json.NewDecoder(io.LimitReader(resp.Body, 1<<20)).Decode(&folders); err != nil {
		return false, err
	}
	for _, f := range folders {
		if strings.EqualFold(f.Name, s.jellyfinX.Name) {
			return true, nil
		}
	}
	return false, nil
}

// handleJellyfinXState reports whether the X library is currently visible.
func (s *Server) handleJellyfinXState(w http.ResponseWriter, r *http.Request) {
	if !s.jellyfinReady() {
		http.Error(w, "jellyfin toggle not configured", http.StatusServiceUnavailable)
		return
	}
	present, err := s.xLibraryPresent(r.Context())
	if err != nil {
		s.logger.Error().Err(err).Msg("jellyfin X state")
		http.Error(w, err.Error(), http.StatusBadGateway)
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"visible": present, "name": s.jellyfinX.Name})
}

type jellyfinXToggleReq struct {
	Visible bool `json:"visible"`
}

// handleJellyfinXToggle adds (visible) or removes (hidden) the X library.
func (s *Server) handleJellyfinXToggle(w http.ResponseWriter, r *http.Request) {
	if !s.jellyfinReady() {
		http.Error(w, "jellyfin toggle not configured", http.StatusServiceUnavailable)
		return
	}
	var req jellyfinXToggleReq
	if err := json.NewDecoder(io.LimitReader(r.Body, 1024)).Decode(&req); err != nil {
		http.Error(w, "bad json: "+err.Error(), http.StatusBadRequest)
		return
	}
	present, err := s.xLibraryPresent(r.Context())
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadGateway)
		return
	}
	if req.Visible == present {
		writeJSON(w, http.StatusOK, map[string]any{"visible": present, "changed": false})
		return
	}

	if req.Visible {
		// Add the library back. collectionType=movies; refreshLibrary triggers
		// a scan of the X folder so its contents reappear.
		q := url.Values{}
		q.Set("name", s.jellyfinX.Name)
		// homevideos + EnableInternetProviders:false → Jellyfin never matches
		// or scrapes these files against TMDB/etc. They show by filename only,
		// with no posters, titles, or fetched metadata. SaveLocalMetadata:false
		// also keeps it from writing .nfo/artwork next to the files.
		q.Set("collectionType", "homevideos")
		q.Set("refreshLibrary", "true")
		bodyJSON := fmt.Sprintf(`{"LibraryOptions":{"EnableInternetProviders":false,"SaveLocalMetadata":false,"EnablePhotos":false,"PathInfos":[{"Path":%q}]}}`, s.jellyfinX.Path)
		resp, err := s.jellyfinRequest(r.Context(), http.MethodPost,
			"/Library/VirtualFolders?"+q.Encode(), strings.NewReader(bodyJSON))
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadGateway)
			return
		}
		defer resp.Body.Close()
		if resp.StatusCode/100 != 2 {
			b, _ := io.ReadAll(io.LimitReader(resp.Body, 512))
			http.Error(w, fmt.Sprintf("jellyfin add library: status %d: %s", resp.StatusCode, b), http.StatusBadGateway)
			return
		}
	} else {
		// Remove the library. refreshLibrary=true is load-bearing: without it
		// Jellyfin drops the VirtualFolder config but leaves an orphaned
		// CollectionFolder behind (because the media-x path still exists), so
		// the library keeps showing in the UI. The refresh reconciles the item
		// DB and purges it.
		q := url.Values{}
		q.Set("name", s.jellyfinX.Name)
		q.Set("refreshLibrary", "true")
		resp, err := s.jellyfinRequest(r.Context(), http.MethodDelete,
			"/Library/VirtualFolders?"+q.Encode(), nil)
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadGateway)
			return
		}
		defer resp.Body.Close()
		if resp.StatusCode/100 != 2 {
			b, _ := io.ReadAll(io.LimitReader(resp.Body, 512))
			http.Error(w, fmt.Sprintf("jellyfin remove library: status %d: %s", resp.StatusCode, b), http.StatusBadGateway)
			return
		}
	}
	s.logger.Info().Bool("visible", req.Visible).Msg("jellyfin X library toggled")
	writeJSON(w, http.StatusOK, map[string]any{"visible": req.Visible, "changed": true})
}
