package anthropic

import (
	"context"
	"fmt"

	"github.com/anthropics/anthropic-sdk-go"
	"github.com/blacktop/ipsw/internal/ai/utils"
)

type Config struct {
	Prompt         string  `json:"prompt"`
	Model          string  `json:"model"`
	Temperature    float64 `json:"temperature"`
	TemperatureSet bool    `json:"-"`
	TopP           float64 `json:"top_p"`
	TopPSet        bool    `json:"-"`
	Stream         bool    `json:"stream"`
}

type Claude struct {
	ctx    context.Context
	conf   *Config
	cli    *anthropic.Client
	models map[string]string
}

func NewClaude(ctx context.Context, conf *Config) (*Claude, error) {
	cli := anthropic.NewClient()
	claude := &Claude{
		ctx:  ctx,
		conf: conf,
		cli:  &cli,
	}
	return claude, nil
}

func (c *Claude) Models() (map[string]string, error) {
	if len(c.models) > 0 {
		return c.models, nil
	}
	if err := c.getModels(); err != nil {
		return nil, fmt.Errorf("claude: failed to get models: %w", err)
	}
	return c.models, nil
}

func (c *Claude) SetModel(model string) error {
	if _, ok := c.models[model]; !ok {
		return fmt.Errorf("model '%s' not found", model)
	}
	c.conf.Model = model
	return nil
}

func (c *Claude) SetModels(models map[string]string) (map[string]string, error) {
	c.models = models
	return c.models, nil
}

// Verify checks that the current model configuration is valid
func (c *Claude) Verify() error {
	if c.conf.Model == "" {
		return fmt.Errorf("no model specified")
	}
	if len(c.models) == 0 {
		if _, err := c.Models(); err != nil {
			return fmt.Errorf("failed to fetch models: %v", err)
		}
	}
	modelID, ok := c.models[c.conf.Model]
	if !ok {
		// Model not found in cache, try refreshing the models list
		c.models = make(map[string]string) // Clear cache to force refresh
		if _, err := c.Models(); err != nil {
			return fmt.Errorf("failed to fetch models: %v", err)
		}
		// Check again after refresh
		modelID, ok = c.models[c.conf.Model]
		if !ok {
			return fmt.Errorf("model '%s' not found in available models", c.conf.Model)
		}
	}
	if modelID == "" {
		return fmt.Errorf("model '%s' has empty ID", c.conf.Model)
	}
	return nil
}

func (c *Claude) getModels() error {
	models, err := c.cli.Models.List(c.ctx, anthropic.ModelListParams{})
	if err != nil {
		return fmt.Errorf("failed to list models: %w", err)
	}
	c.models = make(map[string]string)
	for _, model := range models.Data {
		c.models[model.DisplayName] = model.ID
	}
	if len(c.models) == 0 {
		return fmt.Errorf("no models found")
	}
	return nil
}

func (c *Claude) Chat() (string, error) {
	// Verify model configuration before making API call
	if err := c.Verify(); err != nil {
		return "", fmt.Errorf("invalid model configuration: %w", err)
	}
	if c.conf.TemperatureSet && c.conf.TopPSet {
		return "", fmt.Errorf("claude: temperature and top_p cannot both be set for the same request")
	}

	params := anthropic.MessageNewParams{
		MaxTokens: 1024,
		Messages: []anthropic.MessageParam{
			anthropic.NewUserMessage(anthropic.NewTextBlock(c.conf.Prompt)),
		},
		Model:       anthropic.Model(c.models[c.conf.Model]),
		ServiceTier: anthropic.MessageNewParamsServiceTierAuto,
	}
	if c.conf.TemperatureSet {
		params.Temperature = anthropic.Float(c.conf.Temperature)
	}
	if c.conf.TopPSet {
		params.TopP = anthropic.Float(c.conf.TopP)
	}

	message, err := c.cli.Messages.New(c.ctx, params)
	if err != nil {
		return "", fmt.Errorf("failed to create message: %w", err)
	}

	if len(message.Content) == 0 {
		return "", fmt.Errorf("no content returned from message")
	}

	return utils.Clean(message.Content[0].Text), nil
}

// Close implements the ai.AI interface.
func (c *Claude) Close() error {
	return nil // No specific resources to close for Claude client
}
