package provider

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

	"github.com/rose_cat707/luminary/internal/model"
)

type ForwardRequest struct {
	Endpoint EndpointType
	Method   string
	Body     []byte
	Headers  map[string]string
}

type ForwardResponse struct {
	StatusCode int
	Body       []byte
	TokensIn   int
	TokensOut  int
}

type ProviderConfig struct {
	Type           model.ProviderType
	Category       model.ProviderCategory
	BaseURL        string
	EndpointPaths  map[string]string // endpoint key -> path override
}

func ParseEndpointPaths(jsonStr string) map[string]string {
	out := map[string]string{}
	if jsonStr == "" || jsonStr == "{}" {
		return out
	}
	_ = json.Unmarshal([]byte(jsonStr), &out)
	return out
}

func ParseAllowedEndpoints(jsonStr string, category model.ProviderCategory) []string {
	var out []string
	if jsonStr == "" {
		return DefaultEndpointsForCategory(category)
	}
	_ = json.Unmarshal([]byte(jsonStr), &out)
	if len(out) == 0 {
		return DefaultEndpointsForCategory(category)
	}
	return out
}

func (cfg *ProviderConfig) ResolveEndpointURL(endpoint EndpointType) string {
	override := cfg.EndpointPaths[string(endpoint)]
	defaultPath := DefaultPath(cfg.Type, endpoint)
	return ResolveURL(cfg.BaseURL, override, defaultPath)
}

func ForwardHTTP(ctx context.Context, client *http.Client, method, url string, apiKey string, providerType model.ProviderType, body []byte, extraHeaders map[string]string) (*ForwardResponse, error) {
	var reader io.Reader
	if len(body) > 0 {
		reader = bytes.NewReader(body)
	}
	req, err := http.NewRequestWithContext(ctx, method, url, reader)
	if err != nil {
		return nil, err
	}
	setAuthHeaders(req, apiKey, providerType)
	req.Header.Set("Content-Type", "application/json")
	for k, v := range extraHeaders {
		req.Header.Set(k, v)
	}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	tokensIn, tokensOut := parseUsageJSON(respBody)
	return &ForwardResponse{
		StatusCode: resp.StatusCode,
		Body:       respBody,
		TokensIn:   tokensIn,
		TokensOut:  tokensOut,
	}, nil
}

func setAuthHeaders(req *http.Request, apiKey string, providerType model.ProviderType) {
	switch providerType {
	case model.ProviderAnthropic:
		req.Header.Set("x-api-key", apiKey)
		req.Header.Set("anthropic-version", "2023-06-01")
	case model.ProviderAzureOpenAI:
		if apiKey != "" {
			req.Header.Set("api-key", apiKey)
		}
	case model.ProviderOllama:
		if apiKey != "" {
			req.Header.Set("Authorization", "Bearer "+apiKey)
		}
	default:
		if apiKey != "" {
			req.Header.Set("Authorization", "Bearer "+apiKey)
		}
	}
}

func parseUsageJSON(body []byte) (int, int) {
	var u struct {
		Usage struct {
			PromptTokens     int `json:"prompt_tokens"`
			CompletionTokens int `json:"completion_tokens"`
			InputTokens      int `json:"input_tokens"`
			OutputTokens     int `json:"output_tokens"`
		} `json:"usage"`
	}
	_ = json.Unmarshal(body, &u)
	in := u.Usage.PromptTokens
	out := u.Usage.CompletionTokens
	if in == 0 {
		in = u.Usage.InputTokens
	}
	if out == 0 {
		out = u.Usage.OutputTokens
	}
	return in, out
}

func DefaultBaseURL(providerType model.ProviderType) string {
	switch providerType {
	case model.ProviderAnthropic:
		return "https://api.anthropic.com/v1"
	case model.ProviderAzureOpenAI:
		return "" // must be configured per deployment
	case model.ProviderOpenRouter:
		return "https://openrouter.ai/api/v1"
	case model.ProviderOllama:
		return "http://127.0.0.1:11434/v1"
	default:
		return "https://api.openai.com/v1"
	}
}

func IsOpenAICompatible(t model.ProviderType) bool {
	switch t {
	case model.ProviderOpenAI, model.ProviderCustom, model.ProviderAzureOpenAI, model.ProviderOpenRouter, model.ProviderOllama:
		return true
	default:
		return false
	}
}

func NormalizeBaseURL(baseURL string, providerType model.ProviderType) string {
	if baseURL == "" {
		return DefaultBaseURL(providerType)
	}
	return baseURL
}

func IsEndpointAllowed(allowed []string, endpoint EndpointType) bool {
	for _, a := range allowed {
		if a == string(endpoint) || a == "*" {
			return true
		}
	}
	return false
}

func NewHTTPClient() *http.Client {
	return &http.Client{Timeout: 120 * time.Second}
}

func ValidateCategoryEndpoints(category model.ProviderCategory, allowed []string) error {
	if !model.IsCategorySupported(category) {
		return fmt.Errorf("category %s is not supported yet", category)
	}
	valid := EndpointsForCategory(category)
	validSet := map[string]bool{}
	for _, e := range valid {
		validSet[string(e.Key)] = true
	}
	for _, a := range allowed {
		if a == "*" {
			continue
		}
		if !validSet[a] {
			return fmt.Errorf("endpoint %s is not available for category %s", a, category)
		}
	}
	return nil
}
