package routing

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
)

var (
	// llmBaseURL is the base URL for the LLM API
	llmBaseURL string
)

func init() {
	llmBaseURL = os.Getenv("LOCAL_LLM_BASE_URL")
	if llmBaseURL == "" {
		llmBaseURL = "https://api.riotpiao.com"
	}
}

// AuthType specifies the authentication mechanism
type AuthType string

const (
	// AuthTypeNone - no authentication
	AuthTypeNone AuthType = "none"
	// AuthTypeBearer - Bearer token (JWT, OAuth2)
	AuthTypeBearer AuthType = "bearer"
	// AuthTypeAPIKey - API Key authentication
	AuthTypeAPIKey AuthType = "api-key"
	// AuthTypeCustom - Custom header-based authentication
	AuthTypeCustom AuthType = "custom"
)

// LLMAuth configures authentication for LLM API
type LLMAuth struct {
	// Type of authentication
	Type AuthType `json:"type"`
	
	// Token is the JWT/OAuth2 token for Bearer auth
	Token string `json:"token,omitempty"`
	
	// APIKey is the API key for API Key auth
	APIKey string `json:"apiKey,omitempty"`
	
	// HeaderName is the custom header name for Custom auth
	HeaderName string `json:"headerName,omitempty"`
	
	// HeaderValue is the custom header value for Custom auth
	HeaderValue string `json:"headerValue,omitempty"`
}

// LLMClient is a simple LLM client for routing
type LLMClient struct {
	baseURL    string
	httpClient *http.Client
	auth       *LLMAuth
}

// NewLLMClient creates a new LLM client with default (no) auth
func NewLLMClient() *LLMClient {
	return &LLMClient{
		baseURL:    llmBaseURL,
		httpClient: &http.Client{},
		auth:       nil,
	}
}

// NewLLMClientWithAuth creates a new LLM client with authentication
func NewLLMClientWithAuth(auth *LLMAuth) *LLMClient {
	return &LLMClient{
		baseURL:    llmBaseURL,
		httpClient: &http.Client{},
		auth:       auth,
	}
}

// Name returns the provider name
func (c *LLMClient) Name() string {
	return "riotpiao"
}

// IsAvailable checks if the LLM service is available
func (c *LLMClient) IsAvailable(ctx context.Context) error {
	req, err := http.NewRequestWithContext(ctx, "GET", c.baseURL, nil)
	if err != nil {
		return fmt.Errorf("failed to create request: %w", err)
	}
	
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("LLM service unavailable: %w", err)
	}
	defer resp.Body.Close()
	
	if resp.StatusCode >= 500 {
		return fmt.Errorf("LLM service error: %d", resp.StatusCode)
	}
	
	return nil
}

// llmRequest is the request body for the OpenAI-compatible API
type llmRequest struct {
	Model    string       `json:"model"`
	Messages []llmMessage `json:"messages"`
	Stream   bool         `json:"stream"`
}

type llmMessage struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

// llmResponse is the response from the OpenAI-compatible API
type llmResponse struct {
	Choices []struct {
		Message struct {
			Content string `json:"content"`
		} `json:"message"`
	} `json:"choices"`
}

// Chat sends a chat completion request
func (c *LLMClient) Chat(ctx context.Context, systemPrompt, userMessage string) (string, error) {
	req := llmRequest{
		Model: "reasoning",
		Messages: []llmMessage{
			{Role: "system", Content: systemPrompt},
			{Role: "user", Content: userMessage},
		},
		Stream: false,
	}

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

	httpReq, err := http.NewRequestWithContext(ctx, "POST",
		fmt.Sprintf("%s/v1/chat/completions", c.baseURL),
		bytes.NewReader(reqBody))
	if err != nil {
		return "", fmt.Errorf("failed to create HTTP request: %w", err)
	}

	httpReq.Header.Set("Content-Type", "application/json")
	
	// Apply authentication headers
	if err := c.applyAuth(httpReq); err != nil {
		return "", fmt.Errorf("failed to apply authentication: %w", err)
	}

	resp, err := c.httpClient.Do(httpReq)
	if err != nil {
		return "", fmt.Errorf("failed to connect to LLM API at %s: %w", c.baseURL, err)
	}
	defer resp.Body.Close()

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

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

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

	if len(respObj.Choices) == 0 {
		return "", fmt.Errorf("no choices in response from LLM API")
	}

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

// applyAuth applies authentication to the HTTP request based on config
func (c *LLMClient) applyAuth(req *http.Request) error {
	if c.auth == nil || c.auth.Type == AuthTypeNone {
		return nil
	}

	switch c.auth.Type {
	case AuthTypeBearer:
		if c.auth.Token == "" {
			return fmt.Errorf("bearer token is required but not provided")
		}
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.auth.Token))
		
	case AuthTypeAPIKey:
		if c.auth.APIKey == "" {
			return fmt.Errorf("API key is required but not provided")
		}
		// Common API key header names: X-API-Key, api-key, Authorization
		req.Header.Set("X-API-Key", c.auth.APIKey)
		
	case AuthTypeCustom:
		if c.auth.HeaderName == "" || c.auth.HeaderValue == "" {
			return fmt.Errorf("custom header name and value are required but not provided")
		}
		req.Header.Set(c.auth.HeaderName, c.auth.HeaderValue)
	}

	return nil
}

// UpdateAuth updates the authentication config at runtime
func (c *LLMClient) UpdateAuth(auth *LLMAuth) error {
	if auth == nil {
		return fmt.Errorf("auth config cannot be nil")
	}
	c.auth = auth
	return nil
}

// GetAuth returns the current authentication config
func (c *LLMClient) GetAuth() *LLMAuth {
	return c.auth
}
