package ollama

import (
	"context"
	"fmt"
	"tm/pkg/logger"
)

// Service implements the OllamaService interface
type Service struct {
	client *Client
	config *Config
	logger logger.Logger
}

// NewService creates a new Ollama service
func NewService(client *Client) OllamaService {
	return &Service{
		client: client,
		config: client.config,
		logger: client.logger,
	}
}

// Generate generates text using a specified model and prompt
func (s *Service) Generate(ctx context.Context, req *GenerateRequest) (*GenerateResponse, error) {
	if s.logger != nil {
		s.logger.Info("Generating text", map[string]interface{}{
			"model":         req.Model,
			"prompt_length": len(req.Prompt),
		})
	}

	// Set defaults
	if req.Model == "" {
		req.Model = s.config.DefaultModel
	}

	var resp GenerateResponse
	err := s.client.Post(ctx, "/api/generate", req, &resp)
	if err != nil {
		if s.logger != nil {
			s.logger.Error("Text generation failed", map[string]interface{}{
				"model": req.Model,
				"error": err.Error(),
			})
		}
		return nil, ErrGenerationFailed(err.Error())
	}

	if s.logger != nil {
		s.logger.Info("Text generation completed", map[string]interface{}{
			"model":           req.Model,
			"response_length": len(resp.Response),
			"eval_count":      resp.EvalCount,
		})
	}

	return &resp, nil
}

// GenerateWithOptions generates text with additional options
func (s *Service) GenerateWithOptions(ctx context.Context, req *GenerateRequest, opts *GenerateOptions) (*GenerateResponse, error) {
	if s.logger != nil {
		s.logger.Info("Generating text with options", map[string]interface{}{
			"model":         req.Model,
			"prompt_length": len(req.Prompt),
		})
	}

	// Apply options to request
	if opts != nil {
		if req.Options == nil {
			req.Options = make(map[string]interface{})
		}

		if opts.Temperature != nil {
			req.Options["temperature"] = *opts.Temperature
		}
		if opts.TopP != nil {
			req.Options["top_p"] = *opts.TopP
		}
		if opts.TopK != nil {
			req.Options["top_k"] = *opts.TopK
		}
		if opts.RepeatPenalty != nil {
			req.Options["repeat_penalty"] = *opts.RepeatPenalty
		}
		if opts.Seed != nil {
			req.Options["seed"] = *opts.Seed
		}
		if opts.Stop != nil {
			req.Options["stop"] = opts.Stop
		}
		if opts.Format != "" {
			req.Format = opts.Format
		}

		// Merge additional options
		for k, v := range opts.Options {
			req.Options[k] = v
		}
	}

	return s.Generate(ctx, req)
}

// Chat performs a conversational chat with the model
func (s *Service) Chat(ctx context.Context, req *ChatRequest) (*ChatResponse, error) {
	if s.logger != nil {
		s.logger.Info("Starting chat", map[string]interface{}{
			"model":         req.Model,
			"message_count": len(req.Messages),
		})
	}

	// Set defaults
	if req.Model == "" {
		req.Model = s.config.DefaultModel
	}

	var resp ChatResponse
	err := s.client.Post(ctx, "/api/chat", req, &resp)
	if err != nil {
		if s.logger != nil {
			s.logger.Error("Chat failed", map[string]interface{}{
				"model": req.Model,
				"error": err.Error(),
			})
		}
		return nil, ErrChatFailed(err.Error())
	}

	if s.logger != nil {
		s.logger.Info("Chat completed", map[string]interface{}{
			"model":           req.Model,
			"response_length": len(resp.Message.Content),
			"eval_count":      resp.EvalCount,
		})
	}

	return &resp, nil
}

// Embed generates embeddings for the given text
func (s *Service) Embed(ctx context.Context, req *EmbedRequest) (*EmbedResponse, error) {
	if s.logger != nil {
		s.logger.Info("Generating embeddings", map[string]interface{}{
			"model":       req.Model,
			"text_length": len(req.Prompt),
		})
	}

	var resp EmbedResponse
	err := s.client.Post(ctx, "/api/embeddings", req, &resp)
	if err != nil {
		if s.logger != nil {
			s.logger.Error("Embedding generation failed", map[string]interface{}{
				"model": req.Model,
				"error": err.Error(),
			})
		}
		return nil, ErrEmbeddingFailed(err.Error())
	}

	if s.logger != nil {
		s.logger.Info("Embeddings generated", map[string]interface{}{
			"model":               req.Model,
			"embedding_dimension": len(resp.Embedding),
		})
	}

	return &resp, nil
}

// PullModel pulls a model from the registry
func (s *Service) PullModel(ctx context.Context, model string) (*PullResponse, error) {
	if s.logger != nil {
		s.logger.Info("Pulling model", map[string]interface{}{
			"model": model,
		})
	}

	req := &PullRequest{
		Name: model,
	}

	var resp PullResponse
	err := s.client.Post(ctx, "/api/pull", req, &resp)
	if err != nil {
		if s.logger != nil {
			s.logger.Error("Model pull failed", map[string]interface{}{
				"model": model,
				"error": err.Error(),
			})
		}
		return nil, ErrModelPullFailed(model, err.Error())
	}

	if s.logger != nil {
		s.logger.Info("Model pulled successfully", map[string]interface{}{
			"model":  model,
			"status": resp.Status,
		})
	}

	return &resp, nil
}

// ListModels lists available models
func (s *Service) ListModels(ctx context.Context) (*ListModelsResponse, error) {
	if s.logger != nil {
		s.logger.Debug("Listing models", map[string]interface{}{})
	}

	var resp ListModelsResponse
	err := s.client.Get(ctx, "/api/tags", &resp)
	if err != nil {
		if s.logger != nil {
			s.logger.Error("Failed to list models", map[string]interface{}{
				"error": err.Error(),
			})
		}
		return nil, fmt.Errorf("failed to list models: %w", err)
	}

	if s.logger != nil {
		s.logger.Debug("Models listed", map[string]interface{}{
			"model_count": len(resp.Models),
		})
	}

	return &resp, nil
}

// DeleteModel deletes a model
func (s *Service) DeleteModel(ctx context.Context, model string) (*DeleteResponse, error) {
	if s.logger != nil {
		s.logger.Info("Deleting model", map[string]interface{}{
			"model": model,
		})
	}

	req := &DeleteRequest{
		Name: model,
	}

	var resp DeleteResponse
	err := s.client.Delete(ctx, "/api/delete", req, &resp)
	if err != nil {
		if s.logger != nil {
			s.logger.Error("Model deletion failed", map[string]interface{}{
				"model": model,
				"error": err.Error(),
			})
		}
		return nil, ErrModelDeleteFailed(model, err.Error())
	}

	if s.logger != nil {
		s.logger.Info("Model deleted successfully", map[string]interface{}{
			"model":  model,
			"status": resp.Status,
		})
	}

	return &resp, nil
}

// ShowModel shows model information
func (s *Service) ShowModel(ctx context.Context, model string) (*ShowModelResponse, error) {
	if s.logger != nil {
		s.logger.Debug("Showing model information", map[string]interface{}{
			"model": model,
		})
	}

	req := &ShowModelRequest{
		Name: model,
	}

	var resp ShowModelResponse
	err := s.client.Post(ctx, "/api/show", req, &resp)
	if err != nil {
		if s.logger != nil {
			s.logger.Error("Failed to show model", map[string]interface{}{
				"model": model,
				"error": err.Error(),
			})
		}
		return nil, fmt.Errorf("failed to show model %s: %w", model, err)
	}

	if s.logger != nil {
		s.logger.Debug("Model information retrieved", map[string]interface{}{
			"model": model,
		})
	}

	return &resp, nil
}

// Health checks if the Ollama service is available
func (s *Service) Health(ctx context.Context) error {
	if s.logger != nil {
		s.logger.Debug("Checking Ollama health", map[string]interface{}{})
	}

	var resp HealthResponse
	err := s.client.Get(ctx, "/api/version", &resp)
	if err != nil {
		if s.logger != nil {
			s.logger.Error("Health check failed", map[string]interface{}{
				"error": err.Error(),
			})
		}
		return ErrConnectionFailed(err.Error())
	}

	if s.logger != nil {
		s.logger.Debug("Health check passed", map[string]interface{}{})
	}

	return nil
}

// NewChatBuilder creates a new chat builder for conversational interactions
func (s *Service) NewChatBuilder() ChatBuilder {
	return &chatBuilder{
		service: s,
		request: &ChatRequest{
			Model:    s.config.DefaultModel,
			Messages: make([]Message, 0),
			Options: ChatOptions{
				Temperature: &s.config.DefaultTemperature,
				TopP:        &s.config.DefaultTopP,
			},
		},
	}
}

// chatBuilder implements the ChatBuilder interface
type chatBuilder struct {
	service OllamaService
	request *ChatRequest
}

func (b *chatBuilder) SetModel(model string) ChatBuilder {
	b.request.Model = model
	return b
}

func (b *chatBuilder) SetSystem(system string) ChatBuilder {
	// Add or update system message
	for i, msg := range b.request.Messages {
		if msg.Role == MessageRoleSystem {
			b.request.Messages[i].Content = system
			return b
		}
	}

	// Add new system message at the beginning
	systemMsg := Message{
		Role:    MessageRoleSystem,
		Content: system,
	}
	b.request.Messages = append([]Message{systemMsg}, b.request.Messages...)
	return b
}

func (b *chatBuilder) AddMessage(role MessageRole, content string) ChatBuilder {
	msg := Message{
		Role:    role,
		Content: content,
	}
	b.request.Messages = append(b.request.Messages, msg)
	return b
}

func (b *chatBuilder) SetTemperature(temperature float64) ChatBuilder {
	b.request.Options.Temperature = &temperature
	return b
}

func (b *chatBuilder) SetTopP(topP float64) ChatBuilder {
	b.request.Options.TopP = &topP
	return b
}

func (b *chatBuilder) SetMaxTokens(maxTokens int) ChatBuilder {
	b.request.Options.NumPredict = &maxTokens
	return b
}

func (b *chatBuilder) SetStream(stream bool) ChatBuilder {
	b.request.Stream = &stream
	return b
}

func (b *chatBuilder) SetFormat(format string) ChatBuilder {
	b.request.Format = format
	return b
}

func (b *chatBuilder) SetOptions(options map[string]interface{}) ChatBuilder {
	if b.request.Options.Options == nil {
		b.request.Options.Options = make(map[string]interface{})
	}
	for k, v := range options {
		b.request.Options.Options[k] = v
	}
	return b
}

func (b *chatBuilder) Build() (*ChatRequest, error) {
	if len(b.request.Messages) == 0 {
		return nil, ErrInvalidRequest("at least one message is required")
	}
	return b.request, nil
}

func (b *chatBuilder) Send(ctx context.Context) (*ChatResponse, error) {
	req, err := b.Build()
	if err != nil {
		return nil, err
	}
	return b.service.Chat(ctx, req)
}

func (b *chatBuilder) Continue(ctx context.Context, userMessage string) (*ChatResponse, error) {
	// Add user message
	b.AddMessage(MessageRoleUser, userMessage)

	// Send chat request
	resp, err := b.Send(ctx)
	if err != nil {
		return nil, err
	}

	// Add assistant response to conversation history
	b.AddMessage(MessageRoleAssistant, resp.Message.Content)

	return resp, nil
}
