package metadata

import (
	"context"
	"fmt"
	"log/slog"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"

	"git.vakhrushev.me/av/jellybit/internal/logging"
)

const tvmazeDefaultBaseURL = "https://api.tvmaze.com"

// TVMazeConfig — настройки клиента TVMaze.
type TVMazeConfig struct {
	Proxy   string
	Timeout time.Duration
	BaseURL string // пусто → api.tvmaze.com; задаётся в тестах
}

// TVMaze — клиент TVMaze. Открытое API без ключа (лимит ~20 запросов/10с).
// Покрывает только сериалы; для фильмов поиск возвращает пусто. В externals
// отдаёт TVDB/IMDb-id, который используем как тег папки Jellyfin.
type TVMaze struct {
	baseURL string
	hc      *http.Client
	log     *slog.Logger
}

// NewTVMaze собирает клиент TVMaze (ключ не нужен). logger nil → slog.Default().
func NewTVMaze(cfg TVMazeConfig, logger *slog.Logger) (*TVMaze, error) {
	hc, err := newHTTPClient(cfg.Proxy, cfg.Timeout)
	if err != nil {
		return nil, err
	}
	base := cfg.BaseURL
	if base == "" {
		base = tvmazeDefaultBaseURL
	}
	if logger == nil {
		logger = slog.Default()
	}
	return &TVMaze{baseURL: strings.TrimRight(base, "/"), hc: hc, log: logger}, nil
}

func (t *TVMaze) Name() string { return "tvmaze" }

type tvmazeShow struct {
	ID        int    `json:"id"`
	Name      string `json:"name"`
	Premiered string `json:"premiered"`
	Externals struct {
		TheTVDB int    `json:"thetvdb"`
		IMDb    string `json:"imdb"`
	} `json:"externals"`
}

// Search ищет сериал по названию. Фильмы TVMaze не покрывает — для них
// возвращает пусто. Год не сужаем в запросе (TVMaze не фильтрует по году),
// отбор по году делает вызывающий.
func (t *TVMaze) Search(ctx context.Context, q Query) ([]Candidate, error) {
	if q.Type != Series {
		return nil, nil
	}
	var resp []struct {
		Show tvmazeShow `json:"show"`
	}
	rawURL := t.baseURL + "/search/shows?q=" + url.QueryEscape(q.Title)
	if err := getJSON(ctx, t.hc, t.log, logging.ServiceTVMaze, "search/shows", rawURL, nil, &resp); err != nil {
		return nil, fmt.Errorf("tvmaze search: %w", err)
	}

	out := make([]Candidate, 0, len(resp))
	for _, r := range resp {
		s := r.Show
		c := Candidate{
			Provider: "tvmaze",
			ID:       strconv.Itoa(s.ID),
			Title:    s.Name,
			Year:     yearOf(s.Premiered),
			URL:      "https://www.tvmaze.com/shows/" + strconv.Itoa(s.ID),
		}
		// Тег папки — привычный TVDB-id, если есть; иначе IMDb.
		switch {
		case s.Externals.TheTVDB != 0:
			c.TagProvider, c.TagID = "tvdb", strconv.Itoa(s.Externals.TheTVDB)
		case s.Externals.IMDb != "":
			c.TagProvider, c.TagID = "imdb", s.Externals.IMDb
		}
		out = append(out, c)
	}
	return out, nil
}

type tvmazeEpisode struct {
	Season int `json:"season"`
	Number int `json:"number"`
}

// SeasonEpisodeCounts считает число серий по сезонам (нативный id TVMaze).
func (t *TVMaze) SeasonEpisodeCounts(ctx context.Context, id string) (map[int]int, error) {
	var eps []tvmazeEpisode
	rawURL := t.baseURL + "/shows/" + url.PathEscape(id) + "/episodes"
	if err := getJSON(ctx, t.hc, t.log, logging.ServiceTVMaze, "shows/episodes", rawURL, nil, &eps); err != nil {
		return nil, fmt.Errorf("tvmaze episodes %s: %w", id, err)
	}
	out := map[int]int{}
	for _, e := range eps {
		out[e.Season]++
	}
	return out, nil
}
