package tpdb

import (
	"context"
	"encoding/json"
	"fmt"
	"net/url"

	"git.leaktechnologies.dev/stu/Goondex/internal/model"
)

// Scraper implements the scraper.Scraper interface for TPDB
type Scraper struct {
	client *Client
}

// NewScraper creates a new TPDB scraper
func NewScraper(baseURL, apiKey string) *Scraper {
	return &Scraper{
		client: NewClient(baseURL, apiKey),
	}
}

// Name returns the scraper's unique identifier
func (s *Scraper) Name() string {
	return "tpdb"
}

// SearchPerformers searches for performers by query string
func (s *Scraper) SearchPerformers(ctx context.Context, query string) ([]model.Performer, error) {
	params := url.Values{}
	params.Set("q", query)

	body, err := s.client.get(ctx, "/performers", params)
	if err != nil {
		return nil, fmt.Errorf("failed to search performers: %w", err)
	}

	var apiResp APIResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		return nil, fmt.Errorf("failed to parse response: %w", err)
	}

	var tpdbPerformers []PerformerResponse
	if err := json.Unmarshal(apiResp.Data, &tpdbPerformers); err != nil {
		return nil, fmt.Errorf("failed to parse performers: %w", err)
	}

	performers := make([]model.Performer, 0, len(tpdbPerformers))
	for _, p := range tpdbPerformers {
		performers = append(performers, mapPerformer(p))
	}

	return performers, nil
}

// SearchStudios searches for studios by query string
func (s *Scraper) SearchStudios(ctx context.Context, query string) ([]model.Studio, error) {
	params := url.Values{}
	params.Set("q", query)

	body, err := s.client.get(ctx, "/sites", params)
	if err != nil {
		return nil, fmt.Errorf("failed to search studios: %w", err)
	}

	var apiResp APIResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		return nil, fmt.Errorf("failed to parse response: %w", err)
	}

	var tpdbStudios []StudioResponse
	if err := json.Unmarshal(apiResp.Data, &tpdbStudios); err != nil {
		return nil, fmt.Errorf("failed to parse studios: %w", err)
	}

	studios := make([]model.Studio, 0, len(tpdbStudios))
	for _, st := range tpdbStudios {
		studios = append(studios, mapStudio(st))
	}

	return studios, nil
}

// SearchScenes searches for scenes by query string
func (s *Scraper) SearchScenes(ctx context.Context, query string) ([]model.Scene, error) {
	params := url.Values{}
	params.Set("q", query)

	body, err := s.client.get(ctx, "/scenes", params)
	if err != nil {
		return nil, fmt.Errorf("failed to search scenes: %w", err)
	}

	var apiResp APIResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		return nil, fmt.Errorf("failed to parse response: %w", err)
	}

	var tpdbScenes []SceneResponse
	if err := json.Unmarshal(apiResp.Data, &tpdbScenes); err != nil {
		return nil, fmt.Errorf("failed to parse scenes: %w", err)
	}

	scenes := make([]model.Scene, 0, len(tpdbScenes))
	for _, sc := range tpdbScenes {
		scenes = append(scenes, mapScene(sc))
	}

	return scenes, nil
}

// GetSceneByID retrieves a scene by its remote ID
func (s *Scraper) GetSceneByID(ctx context.Context, remoteID string) (*model.Scene, error) {
	body, err := s.client.get(ctx, "/scenes/"+remoteID, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get scene: %w", err)
	}

	var apiResp APIResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		return nil, fmt.Errorf("failed to parse response: %w", err)
	}

	var tpdbScene SceneResponse
	if err := json.Unmarshal(apiResp.Data, &tpdbScene); err != nil {
		return nil, fmt.Errorf("failed to parse scene: %w", err)
	}

	scene := mapScene(tpdbScene)
	return &scene, nil
}

// GetPerformerByID retrieves a performer by its remote ID
func (s *Scraper) GetPerformerByID(ctx context.Context, remoteID string) (*model.Performer, error) {
	body, err := s.client.get(ctx, "/performers/"+remoteID, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get performer: %w", err)
	}

	var apiResp APIResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		return nil, fmt.Errorf("failed to parse response: %w", err)
	}

	var tpdbPerformer PerformerResponse
	if err := json.Unmarshal(apiResp.Data, &tpdbPerformer); err != nil {
		return nil, fmt.Errorf("failed to parse performer: %w", err)
	}

	performer := mapPerformer(tpdbPerformer)
	return &performer, nil
}

// GetStudioByID retrieves a studio by its remote ID
func (s *Scraper) GetStudioByID(ctx context.Context, remoteID string) (*model.Studio, error) {
	body, err := s.client.get(ctx, "/sites/"+remoteID, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to get studio: %w", err)
	}

	var apiResp APIResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		return nil, fmt.Errorf("failed to parse response: %w", err)
	}

	var tpdbStudio StudioResponse
	if err := json.Unmarshal(apiResp.Data, &tpdbStudio); err != nil {
		return nil, fmt.Errorf("failed to parse studio: %w", err)
	}

	studio := mapStudio(tpdbStudio)
	return &studio, nil
}

// ListPerformers fetches all performers with pagination
func (s *Scraper) ListPerformers(ctx context.Context, page int) ([]model.Performer, *MetaData, error) {
	params := url.Values{}
	params.Set("page", fmt.Sprintf("%d", page))

	body, err := s.client.get(ctx, "/performers", params)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to list performers: %w", err)
	}

	var apiResp APIResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		return nil, nil, fmt.Errorf("failed to parse response: %w", err)
	}

	var tpdbPerformers []PerformerResponse
	if err := json.Unmarshal(apiResp.Data, &tpdbPerformers); err != nil {
		return nil, nil, fmt.Errorf("failed to parse performers: %w", err)
	}

	performers := make([]model.Performer, 0, len(tpdbPerformers))
	for _, p := range tpdbPerformers {
		performers = append(performers, mapPerformer(p))
	}

	return performers, apiResp.Meta, nil
}

// ListStudios fetches all studios with pagination
func (s *Scraper) ListStudios(ctx context.Context, page int) ([]model.Studio, *MetaData, error) {
	params := url.Values{}
	params.Set("page", fmt.Sprintf("%d", page))

	body, err := s.client.get(ctx, "/sites", params)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to list studios: %w", err)
	}

	var apiResp APIResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		return nil, nil, fmt.Errorf("failed to parse response: %w", err)
	}

	var tpdbStudios []StudioResponse
	if err := json.Unmarshal(apiResp.Data, &tpdbStudios); err != nil {
		return nil, nil, fmt.Errorf("failed to parse studios: %w", err)
	}

	studios := make([]model.Studio, 0, len(tpdbStudios))
	for _, st := range tpdbStudios {
		studios = append(studios, mapStudio(st))
	}

	return studios, apiResp.Meta, nil
}

// ListScenes fetches all scenes with pagination
func (s *Scraper) ListScenes(ctx context.Context, page int) ([]model.Scene, *MetaData, error) {
	params := url.Values{}
	params.Set("page", fmt.Sprintf("%d", page))

	body, err := s.client.get(ctx, "/scenes", params)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to list scenes: %w", err)
	}

	var apiResp APIResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		return nil, nil, fmt.Errorf("failed to parse response: %w", err)
	}

	var tpdbScenes []SceneResponse
	if err := json.Unmarshal(apiResp.Data, &tpdbScenes); err != nil {
		return nil, nil, fmt.Errorf("failed to parse scenes: %w", err)
	}

	scenes := make([]model.Scene, 0, len(tpdbScenes))
	for _, sc := range tpdbScenes {
		scenes = append(scenes, mapScene(sc))
	}

	return scenes, apiResp.Meta, nil
}
