package imdb

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

	"github.com/enmanuel/agents/internal/config"
	"github.com/enmanuel/agents/tools"
)

// SearchResult represents a single movie/series result from OMDb API.
type SearchResult struct {
	Title  string `json:"Title"`
	Year   string `json:"Year"`
	ImdbID string `json:"imdbID"`
	Type   string `json:"Type"`
	Poster string `json:"Poster"`
}

// SearchResponse represents the full response from OMDb search endpoint.
type SearchResponse struct {
	Search       []SearchResult `json:"Search"`
	TotalResults string         `json:"totalResults"`
	Response     string         `json:"Response"`
	Error        string         `json:"Error"`
}

// NewIMDbSearch creates an imdb_search tool that searches movies on IMDb via OMDb API.
// Returns up to 5 results with title, year, type, poster URL, and IMDb ID.
// Requires API key from http://www.omdbapi.com/
func NewIMDbSearch(cfg config.IMDbToolCfg) tools.Tool {
	timeout := cfg.Timeout
	if timeout == 0 {
		timeout = 10 * time.Second
	}
	client := &http.Client{Timeout: timeout}

	return tools.Tool{
		Def: tools.Def{
			Name:        "imdb_search",
			Description: "Search for movies or series on IMDb by title. Returns up to 5 results with title, year, type, poster image URL, and IMDb ID.",
			Parameters: []tools.Param{
				{Name: "query", Type: "string", Description: "The movie or series title to search for", Required: true},
				{Name: "year", Type: "integer", Description: "Optional year to filter results (e.g., 2020)", Required: false},
			},
		},
		Exec: func(ctx context.Context, args map[string]any) tools.Result {
			query := tools.GetString(args, "query")
			if query == "" {
				return tools.Result{Err: fmt.Errorf("imdb_search: query is required")}
			}

			// Get API key from config or env var
			apiKey := cfg.APIKey
			if apiKey == "" && cfg.APIKeyEnv != "" {
				apiKey = getEnvVar(cfg.APIKeyEnv)
			}
			if apiKey == "" {
				return tools.Result{Err: fmt.Errorf("imdb_search: API key not configured (set imdb.api_key or imdb.api_key_env in config)")}
			}

			// Build search URL
			searchURL := buildSearchURL(apiKey, query, args)

			req, err := http.NewRequestWithContext(ctx, http.MethodGet, searchURL, nil)
			if err != nil {
				return tools.Result{Err: fmt.Errorf("imdb_search: %w", err)}
			}

			resp, err := client.Do(req)
			if err != nil {
				return tools.Result{Err: fmt.Errorf("imdb_search: %w", err)}
			}
			defer resp.Body.Close()

			if resp.StatusCode != http.StatusOK {
				return tools.Result{Err: fmt.Errorf("imdb_search: HTTP %d", resp.StatusCode)}
			}

			body, err := io.ReadAll(io.LimitReader(resp.Body, 64*1024))
			if err != nil {
				return tools.Result{Err: fmt.Errorf("imdb_search: read body: %w", err)}
			}

			var searchResp SearchResponse
			if err := json.Unmarshal(body, &searchResp); err != nil {
				return tools.Result{Err: fmt.Errorf("imdb_search: parse response: %w", err)}
			}

			if searchResp.Response == "False" {
				return tools.Result{Output: fmt.Sprintf("No se encontraron resultados para '%s'. Error: %s", query, searchResp.Error)}
			}

			// Format results (limit to first 5)
			output := formatResults(searchResp.Search, query)
			return tools.Result{Output: output}
		},
	}
}

// buildSearchURL constructs the OMDb API search URL with query parameters.
func buildSearchURL(apiKey, query string, args map[string]any) string {
	params := url.Values{}
	params.Set("apikey", apiKey)
	params.Set("s", query)
	params.Set("type", "movie") // default to movies, could be made configurable

	// Add year filter if provided
	if year := tools.GetInt(args, "year"); year > 0 {
		params.Set("y", fmt.Sprintf("%d", year))
	}

	return fmt.Sprintf("https://www.omdbapi.com/?%s", params.Encode())
}

// formatResults converts search results into a readable text format.
func formatResults(results []SearchResult, query string) string {
	if len(results) == 0 {
		return fmt.Sprintf("No se encontraron películas para '%s'", query)
	}

	var builder strings.Builder
	builder.WriteString(fmt.Sprintf("🎬 Resultados de IMDb para '%s':\n\n", query))

	// Limit to 5 results
	limit := 5
	if len(results) < limit {
		limit = len(results)
	}

	for i := 0; i < limit; i++ {
		r := results[i]
		builder.WriteString(fmt.Sprintf("%d. **%s** (%s)\n", i+1, r.Title, r.Year))
		builder.WriteString(fmt.Sprintf("   • Tipo: %s\n", r.Type))
		builder.WriteString(fmt.Sprintf("   • IMDb ID: %s\n", r.ImdbID))

		if r.Poster != "" && r.Poster != "N/A" {
			builder.WriteString(fmt.Sprintf("   • Poster: %s\n", r.Poster))
		} else {
			builder.WriteString("   • Poster: No disponible\n")
		}

		builder.WriteString(fmt.Sprintf("   • Link: https://www.imdb.com/title/%s/\n", r.ImdbID))

		if i < limit-1 {
			builder.WriteString("\n")
		}
	}

	if len(results) > 5 {
		builder.WriteString(fmt.Sprintf("\n... y %d resultado(s) más", len(results)-5))
	}

	return builder.String()
}

// getEnvVar retrieves an environment variable by name.
func getEnvVar(name string) string {
	return os.Getenv(name)
}
