package ai

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"
)

// DeepSeekAPI is the client for the DeepSeek chat completion API
type DeepSeekAPI struct {
	apiKey     string
	model      string
	maxTokens  int
	temperature float64
	httpClient *http.Client
}

// chatMessage represents a message in the chat completion request
type chatMessage struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

// chatCompletionRequest represents the API request body
type chatCompletionRequest struct {
	Model       string        `json:"model"`
	Messages    []chatMessage `json:"messages"`
	MaxTokens   int           `json:"max_tokens"`
	Temperature float64       `json:"temperature"`
}

// chatCompletionResponse represents the API response
type chatCompletionResponse struct {
	ID      string                 `json:"id"`
	Object  string                 `json:"object"`
	Created int64                  `json:"created"`
	Model   string                 `json:"model"`
	Choices []chatCompletionChoice `json:"choices"`
	Usage   *struct {
		PromptTokens     int `json:"prompt_tokens"`
		CompletionTokens int `json:"completion_tokens"`
		TotalTokens      int `json:"total_tokens"`
	} `json:"usage,omitempty"`
	Error *struct {
		Message string `json:"message"`
		Type    string `json:"type"`
		Code    string `json:"code"`
	} `json:"error,omitempty"`
}

// chatCompletionChoice represents a single choice in the response
type chatCompletionChoice struct {
	Index        int          `json:"index"`
	Message      chatMessage  `json:"message"`
	FinishReason string       `json:"finish_reason"`
}

const deepSeekEndpoint = "https://api.deepseek.com/v1/chat/completions"

// NewDeepSeekAPI creates a new DeepSeek API client
func NewDeepSeekAPI(apiKey, model string, maxTokens int, temperature float64) *DeepSeekAPI {
	return &DeepSeekAPI{
		apiKey:      apiKey,
		model:       model,
		maxTokens:   maxTokens,
		temperature: temperature,
		httpClient: &http.Client{
			Timeout: 30 * time.Second,
		},
	}
}

// ChatCompletion sends a chat completion request to DeepSeek
func (d *DeepSeekAPI) ChatCompletion(messages []chatMessage) (string, error) {
	reqBody := chatCompletionRequest{
		Model:       d.model,
		Messages:    messages,
		MaxTokens:   d.maxTokens,
		Temperature: d.temperature,
	}

	body, err := json.Marshal(reqBody)
	if err != nil {
		return "", fmt.Errorf("failed to marshal request: %w", err)
	}

	req, err := http.NewRequest(http.MethodPost, deepSeekEndpoint, bytes.NewReader(body))
	if err != nil {
		return "", fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+d.apiKey)
	req.Header.Set("Content-Type", "application/json")

	resp, err := d.httpClient.Do(req)
	if err != nil {
		return "", fmt.Errorf("api request failed: %w", err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("failed to read response: %w", err)
	}

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("api returned status %d: %s", resp.StatusCode, string(respBody))
	}

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

	if apiResp.Error != nil {
		return "", fmt.Errorf("api error: %s (type: %s, code: %s)",
			apiResp.Error.Message, apiResp.Error.Type, apiResp.Error.Code)
	}

	if len(apiResp.Choices) == 0 {
		return "", fmt.Errorf("no choices in api response")
	}

	return apiResp.Choices[0].Message.Content, nil
}

// ClassificationResult holds the parsed DeepSeek response
type ClassificationResult struct {
	Folder   string `json:"folder"`
	Score    int    `json:"score"`
	Context  string `json:"context"`
}

// ParseClassification parses the JSON response from DeepSeek into a ClassificationResult
func ParseClassification(content string) (*ClassificationResult, error) {
	var result ClassificationResult
	if err := json.Unmarshal([]byte(content), &result); err != nil {
		return nil, fmt.Errorf("failed to parse classification JSON: %w", err)
	}

	// Validate folder name
	validFolders := map[string]bool{
		"Important":     true,
		"eCommerce":     true,
		"Notifications": true,
		"Finance":       true,
		"Social":        true,
		"Spam":          true,
		"Other":         true,
	}
	if !validFolders[result.Folder] {
		return nil, fmt.Errorf("invalid folder in classification: %q", result.Folder)
	}

	// Validate score
	if result.Score < 1 || result.Score > 100 {
		return nil, fmt.Errorf("invalid confidence score: %d (must be 1-100)", result.Score)
	}

	return &result, nil
}
