package agentteambridge

import (
	"bytes"
	"context"
	"fmt"
	"net/http"
	"strings"
	"time"
)

type WireEndpoint struct {
	BaseURL string
	Path    string
	Headers map[string]string
}

type ForwardProvider struct {
	name               string
	headers            map[string]string
	wireAPIs           []string
	tokenPool          *TokenPool
	modelCache         *ModelCache
	httpClient         *http.Client
	chatEndpoint       *WireEndpoint
	anthropicEndpoint  *WireEndpoint
	responsesEndpoint  *WireEndpoint
	embeddingsEndpoint *WireEndpoint
}

type ForwardProviderConfig struct {
	Name               string
	BaseURL            string
	Headers            map[string]string
	WireAPIs           []string
	TokenPool          *TokenPool
	ModelCache         *ModelCache
	HTTPClient         *http.Client
	ChatEndpoint       *WireEndpoint
	AnthropicEndpoint  *WireEndpoint
	ResponsesEndpoint  *WireEndpoint
	EmbeddingsEndpoint *WireEndpoint
}

func NewForwardProvider(cfg ForwardProviderConfig) *ForwardProvider {
	client := cfg.HTTPClient
	if client == nil {
		client = &http.Client{Timeout: 10 * time.Minute}
	}
	wireAPIs := cfg.WireAPIs
	if len(wireAPIs) == 0 {
		wireAPIs = []string{WireAIOpenAI}
	}
	return &ForwardProvider{
		name:               strings.TrimSpace(cfg.Name),
		headers:            cfg.Headers,
		wireAPIs:           wireAPIs,
		tokenPool:          cfg.TokenPool,
		modelCache:         cfg.ModelCache,
		httpClient:         client,
		chatEndpoint:       normalizeWireEndpoint(cfg.BaseURL, "/chat/completions", cfg.Headers, cfg.ChatEndpoint),
		anthropicEndpoint:  normalizeWireEndpoint(cfg.BaseURL, "/v1/messages", cfg.Headers, cfg.AnthropicEndpoint),
		responsesEndpoint:  normalizeWireEndpoint(cfg.BaseURL, "/responses", cfg.Headers, cfg.ResponsesEndpoint),
		embeddingsEndpoint: normalizeWireEndpoint(cfg.BaseURL, "/embeddings", cfg.Headers, cfg.EmbeddingsEndpoint),
	}
}

func (provider *ForwardProvider) Name() string { return provider.name }

func (provider *ForwardProvider) Models() []string {
	if provider.modelCache == nil {
		return nil
	}
	return provider.modelCache.Models()
}

func (provider *ForwardProvider) ChatCompletion(ctx context.Context, headers http.Header, body []byte) (*http.Response, error) {
	if !provider.supportsWireAPI(WireAIOpenAI) {
		return nil, ErrWireAPIUnsupported
	}
	return provider.do(ctx, http.MethodPost, provider.chatEndpoint, headers, body)
}

func (provider *ForwardProvider) Messages(ctx context.Context, headers http.Header, body []byte) (*http.Response, error) {
	if !provider.supportsWireAPI(WireAPIAnthropic) {
		return nil, ErrWireAPIUnsupported
	}
	return provider.do(ctx, http.MethodPost, provider.anthropicEndpoint, headers, body)
}

func (provider *ForwardProvider) CreateResponse(ctx context.Context, headers http.Header, body []byte) (*http.Response, error) {
	if !provider.supportsWireAPI(WireAPIResponses) {
		return nil, ErrWireAPIUnsupported
	}
	return provider.do(ctx, http.MethodPost, provider.responsesEndpoint, headers, body)
}

func (provider *ForwardProvider) CreateEmbedding(ctx context.Context, headers http.Header, body []byte) (*http.Response, error) {
	if !provider.supportsWireAPI(WireAIOpenAI) {
		return nil, ErrWireAPIUnsupported
	}
	return provider.do(ctx, http.MethodPost, provider.embeddingsEndpoint, headers, body)
}

func (provider *ForwardProvider) supportsWireAPI(wireAPI string) bool {
	for _, supported := range provider.wireAPIs {
		if strings.EqualFold(strings.TrimSpace(supported), strings.TrimSpace(wireAPI)) {
			return true
		}
	}
	return false
}

func normalizeWireEndpoint(defaultBaseURL, defaultPath string, defaultHeaders map[string]string, endpoint *WireEndpoint) *WireEndpoint {
	baseURL := strings.TrimRight(strings.TrimSpace(defaultBaseURL), "/")
	path := defaultPath
	headers := defaultHeaders
	if endpoint != nil {
		if trimmed := strings.TrimRight(strings.TrimSpace(endpoint.BaseURL), "/"); trimmed != "" {
			baseURL = trimmed
		}
		if trimmed := strings.TrimSpace(endpoint.Path); trimmed != "" {
			path = trimmed
		}
		if len(endpoint.Headers) > 0 {
			headers = endpoint.Headers
		}
	}
	if baseURL == "" {
		return nil
	}
	return &WireEndpoint{BaseURL: baseURL, Path: path, Headers: headers}
}

func (provider *ForwardProvider) do(ctx context.Context, method string, endpoint *WireEndpoint, headers http.Header, body []byte) (*http.Response, error) {
	if provider == nil {
		return nil, fmt.Errorf("provider is nil")
	}
	if endpoint == nil {
		return nil, ErrWireAPIUnsupported
	}
	token, ok := provider.tokenPool.SelectToken()
	if !ok {
		return nil, fmt.Errorf("no provider token available")
	}
	req, err := http.NewRequestWithContext(ctx, method, endpoint.BaseURL+endpoint.Path, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	req.Header = headers.Clone()
	req.Header.Del("Authorization")
	req.Header.Del("X-API-Key")
	req.Header.Del("API-Key")
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("X-API-Key", token)
	req.Header.Set("API-Key", token)
	for k, v := range provider.headers {
		req.Header.Set(k, v)
	}
	for k, v := range endpoint.Headers {
		req.Header.Set(k, v)
	}
	return provider.httpClient.Do(req)
}
