package providers

import (
	"chat-switchboard/capabilities"
	"chat-switchboard/models"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"
)

// OpenRouterProvider handles the OpenRouter unified API.
// OpenRouter is OpenAI-compatible with 300+ models, per-model pricing,
// and architecture metadata. Requires HTTP-Referer and X-Title headers.
//
// Ported from ai-editor js/providers/openrouter.js
type OpenRouterProvider struct{}

func (p *OpenRouterProvider) ID() string { return "openrouter" }

// ── Chat Completion ─────────────────────────
// Delegates to OpenAI provider — OpenRouter is fully OAI-compatible.

func (p *OpenRouterProvider) ChatCompletion(ctx context.Context, cfg ProviderConfig, req CompletionRequest) (*CompletionResponse, error) {
	oai := &OpenAIProvider{}
	return oai.ChatCompletion(ctx, cfg, req)
}

func (p *OpenRouterProvider) StreamCompletion(ctx context.Context, cfg ProviderConfig, req CompletionRequest) (<-chan StreamEvent, error) {
	oai := &OpenAIProvider{}
	return oai.StreamCompletion(ctx, cfg, req)
}

func (p *OpenRouterProvider) Embed(ctx context.Context, cfg ProviderConfig, req EmbeddingRequest) (*EmbeddingResponse, error) {
	oai := &OpenAIProvider{}
	return oai.Embed(ctx, cfg, req)
}

// ── List Models ─────────────────────────────
// OpenRouter /v1/models returns architecture, pricing, context_length.

func (p *OpenRouterProvider) ListModels(ctx context.Context, cfg ProviderConfig) ([]Model, error) {
	endpoint := strings.TrimSuffix(cfg.Endpoint, "/") + "/models"

	httpReq, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil)
	if err != nil {
		return nil, err
	}
	if cfg.APIKey != "" {
		httpReq.Header.Set("Authorization", "Bearer "+cfg.APIKey)
	}
	for k, v := range cfg.CustomHeaders {
		httpReq.Header.Set(k, v)
	}

	resp, err := cfg.Client().Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("openrouter list models: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		b, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("openrouter list models: HTTP %d: %s", resp.StatusCode, string(b))
	}

	var result orModelsResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, fmt.Errorf("openrouter decode models: %w", err)
	}

	out := make([]Model, 0, len(result.Data))
	for _, m := range result.Data {
		// Heuristic inference from model ID
		caps := capabilities.InferCapabilities(m.ID)

		// Overlay context length from OpenRouter metadata
		if m.ContextLength > 0 && caps.MaxContext == 0 {
			caps.MaxContext = m.ContextLength
		}

		// Overlay max output from OpenRouter if available
		if m.TopProvider.MaxCompletionTokens > 0 && caps.MaxOutputTokens == 0 {
			caps.MaxOutputTokens = m.TopProvider.MaxCompletionTokens
		}

		// Vision from architecture modality
		arch := m.Architecture
		if arch.Modality == "multimodal" {
			caps.Vision = true
		}

		// Streaming is universal on OpenRouter
		caps.Streaming = true

		// Parse pricing (OpenRouter uses per-token strings, convert to per-1M)
		var pricing *models.ModelPricing
		if m.Pricing.Prompt != "" {
			inputPerToken, _ := strconv.ParseFloat(m.Pricing.Prompt, 64)
			outputPerToken, _ := strconv.ParseFloat(m.Pricing.Completion, 64)
			if inputPerToken > 0 || outputPerToken > 0 {
				pricing = &models.ModelPricing{
					InputPerM:  inputPerToken * 1_000_000,
					OutputPerM: outputPerToken * 1_000_000,
				}
			}
		}

		// Owner from model ID prefix (e.g. "anthropic/claude-3-opus" → "anthropic")
		ownedBy := ""
		if idx := strings.Index(m.ID, "/"); idx >= 0 {
			ownedBy = m.ID[:idx]
		}

		name := m.Name
		if name == "" {
			name = m.ID
		}

		out = append(out, Model{
			ID:           m.ID,
			Name:         name,
			OwnedBy:      ownedBy,
			Capabilities: caps,
			Pricing:      pricing,
		})
	}
	return out, nil
}

// ── OpenRouter Wire Types ───────────────────

type orModelsResponse struct {
	Data []orModel `json:"data"`
}

type orModel struct {
	ID            string        `json:"id"`
	Name          string        `json:"name"`
	Description   string        `json:"description"`
	ContextLength int           `json:"context_length"`
	Architecture  orArch        `json:"architecture"`
	Pricing       orPricing     `json:"pricing"`
	TopProvider   orTopProvider `json:"top_provider"`
}

type orArch struct {
	Modality       string `json:"modality"`
	Tokenizer      string `json:"tokenizer"`
	InstructType   string `json:"instruct_type"`
}

type orPricing struct {
	Prompt     string `json:"prompt"`     // per-token cost as string
	Completion string `json:"completion"` // per-token cost as string
}

type orTopProvider struct {
	MaxCompletionTokens int  `json:"max_completion_tokens"`
	IsModerated         bool `json:"is_moderated"`
}
