// Package adapters provides textgen provider adapters for various AI services.
package adapters

import (
	"context"
	"fmt"
	"strings"

	"{{GO_MODULE}}/pkg/textgen"
	"google.golang.org/genai"
)

const (
	// IMPORTANT: Always use gemini-3-flash-preview for text generation.
	// DO NOT use gemini-2.5-flash or any 2.x models.
	defaultGeminiTextModel = "gemini-3-flash-preview"
)

// GeminiTextProvider implements textgen.TextGenerator using Gemini API.
type GeminiTextProvider struct {
	client *genai.Client
	model  string
}

// GeminiTextConfig holds configuration for the Gemini text provider.
type GeminiTextConfig struct {
	// APIKey for Gemini API (required if Client is nil)
	APIKey string

	// Client is an existing genai.Client (optional, takes precedence over APIKey)
	Client *genai.Client

	// Model to use for text generation (default: gemini-3-flash-preview)
	// IMPORTANT: DO NOT use gemini-2.5-flash or any 2.x models.
	Model string
}

// NewGeminiTextProvider creates a new Gemini text generation provider.
func NewGeminiTextProvider(ctx context.Context, cfg GeminiTextConfig) (*GeminiTextProvider, error) {
	var client *genai.Client
	var err error

	if cfg.Client != nil {
		client = cfg.Client
	} else if cfg.APIKey != "" {
		client, err = genai.NewClient(ctx, &genai.ClientConfig{
			APIKey:  cfg.APIKey,
			Backend: genai.BackendGeminiAPI,
		})
		if err != nil {
			return nil, fmt.Errorf("create genai client: %w", err)
		}
	} else {
		return nil, fmt.Errorf("%w: APIKey or Client required", textgen.ErrInvalidConfig)
	}

	model := cfg.Model
	if model == "" {
		model = defaultGeminiTextModel
	}

	return &GeminiTextProvider{
		client: client,
		model:  model,
	}, nil
}

// Name implements textgen.Provider.
func (p *GeminiTextProvider) Name() string {
	return "gemini"
}

// Health implements textgen.Provider.
func (p *GeminiTextProvider) Health(ctx context.Context) error {
	// Try a minimal request to verify the API is working
	_, err := p.client.Models.Get(ctx, p.model, nil)
	if err != nil {
		return fmt.Errorf("gemini health check: %w", err)
	}
	return nil
}

// GenerateText implements textgen.TextGenerator.
func (p *GeminiTextProvider) GenerateText(ctx context.Context, req textgen.TextRequest) (*textgen.TextResponse, error) {
	model := req.Model
	if model == "" {
		model = p.model
	}

	// Build content from request
	var content []*genai.Content

	// Add system prompt if provided
	if req.SystemPrompt != "" {
		content = append(content, &genai.Content{
			Role: "user",
			Parts: []*genai.Part{
				{Text: "System: " + req.SystemPrompt},
			},
		})
	}

	// Add messages if provided (multi-turn conversation)
	if len(req.Messages) > 0 {
		for _, msg := range req.Messages {
			role := msg.Role
			if role == "assistant" {
				role = "model"
			}
			content = append(content, &genai.Content{
				Role: role,
				Parts: []*genai.Part{
					{Text: msg.Content},
				},
			})
		}
	} else if req.Prompt != "" {
		// Single prompt
		content = append(content, &genai.Content{
			Role: "user",
			Parts: []*genai.Part{
				{Text: req.Prompt},
			},
		})
	}

	// Configure generation
	var config *genai.GenerateContentConfig
	if req.MaxTokens > 0 || req.Temperature > 0 {
		config = &genai.GenerateContentConfig{}
		if req.MaxTokens > 0 {
			config.MaxOutputTokens = int32(req.MaxTokens)
		}
		if req.Temperature > 0 {
			temp := float32(req.Temperature)
			config.Temperature = &temp
		}
	}

	// Call Gemini API
	resp, err := p.client.Models.GenerateContent(ctx, model, content, config)
	if err != nil {
		return nil, classifyGeminiError(err)
	}

	// Extract text from response
	var responseText string
	if resp != nil && len(resp.Candidates) > 0 && resp.Candidates[0].Content != nil {
		for _, part := range resp.Candidates[0].Content.Parts {
			if part.Text != "" {
				responseText += part.Text
			}
		}
	}

	if responseText == "" {
		return nil, fmt.Errorf("empty response from Gemini")
	}

	// Build response
	result := &textgen.TextResponse{
		Text: responseText,
	}

	// Add usage if available
	if resp.UsageMetadata != nil {
		result.Usage = &textgen.Usage{
			PromptTokens:     int(resp.UsageMetadata.PromptTokenCount),
			CompletionTokens: int(resp.UsageMetadata.CandidatesTokenCount),
			TotalTokens:      int(resp.UsageMetadata.TotalTokenCount),
		}
	}

	return result, nil
}

// classifyGeminiError converts Gemini errors to textgen sentinel errors.
func classifyGeminiError(err error) error {
	if err == nil {
		return nil
	}

	errStr := strings.ToLower(err.Error())

	// Check for common error patterns
	switch {
	case strings.Contains(errStr, "quota") || strings.Contains(errStr, "429"):
		return fmt.Errorf("%w: %v", textgen.ErrQuotaExceeded, err)
	case strings.Contains(errStr, "rate") || strings.Contains(errStr, "limit"):
		return fmt.Errorf("%w: %v", textgen.ErrRateLimited, err)
	case strings.Contains(errStr, "safety") || strings.Contains(errStr, "blocked"):
		return fmt.Errorf("%w: %v", textgen.ErrContentBlocked, err)
	case strings.Contains(errStr, "timeout") || strings.Contains(errStr, "deadline"):
		return fmt.Errorf("%w: %v", textgen.ErrTimeout, err)
	default:
		return err
	}
}

// Compile-time interface check
var _ textgen.TextGenerator = (*GeminiTextProvider)(nil)
