package openai

import (
	"fmt"
	edgeservice "iop/apps/edge/internal/service"
	"iop/packages/go/config"
	"path/filepath"
	"strings"
)

// routeUsesProviderTunnel reports whether the resolved dispatch targets an
// OpenAI-compatible provider that serves raw tunnel passthrough. Provider-pool
// catalog routes and openai_compat/vllm type routes qualify; CLI and other
// legacy adapters keep the normalized RunEvent path.
func routeUsesProviderTunnel(d routeDispatch) bool {
	if d.ProviderPool {
		return true
	}
	switch strings.TrimSpace(d.Adapter) {
	case "openai_compat", "vllm":
		return true
	default:
		return false
	}
}

func routeSupportsNativeToolCalls(dispatch routeDispatch) bool {
	if dispatch.ProviderPool {
		return true
	}
	switch strings.TrimSpace(dispatch.Adapter) {
	case "openai_compat", "vllm", "ollama":
		return true
	default:
		return false
	}
}

func (s *Server) resolveAdapter() string {
	if s.cfg.Adapter != "" {
		return s.cfg.Adapter
	}
	return "ollama"
}

func (s *Server) resolveTarget(model string) string {
	if s.cfg.Target != "" {
		return s.cfg.Target
	}
	return strings.TrimSpace(model)
}

// routeDispatch holds fully-resolved dispatch parameters for a single request.
type routeDispatch struct {
	NodeRef           string
	Adapter           string
	Target            string
	SessionID         string
	TimeoutSec        int
	MaxQueue          int
	QueueTimeoutMS    int
	WorkspaceRequired bool
	// ProviderPool is true when the request model matched a provider-pool
	// catalog entry. Adapter and Target are empty; the service layer resolves
	// them per-candidate and rewrites Target after admission.
	ProviderPool bool
}

// resolveRoute returns the first catalog entry whose Model matches model.
// Entries with an empty Target are skipped.
func (s *Server) resolveRoute(model string) *config.OpenAIRouteEntry {
	model = strings.TrimSpace(model)
	if model == "" {
		return nil
	}
	for i := range s.cfg.ModelRoutes {
		r := &s.cfg.ModelRoutes[i]
		if strings.TrimSpace(r.Model) == model && r.Target != "" {
			return r
		}
	}
	return nil
}

// findProviderPoolEntry returns the catalog entry matching model, or nil.
func (s *Server) findProviderPoolEntry(model string) *config.ModelCatalogEntry {
	model = strings.TrimSpace(model)
	if model == "" {
		return nil
	}
	modelCatalog := s.modelCatalogSnapshot()
	for i := range modelCatalog {
		if modelCatalog[i].ID == model {
			entry := modelCatalog[i]
			return &entry
		}
	}
	return nil
}

// resolveRouteDispatch returns fully-resolved dispatch params for model.
// Priority: provider-pool catalog → legacy model_routes → single-target fallback.
// Returns (dispatch, true) on success; (zero, false) when no target can be resolved.
func (s *Server) resolveRouteDispatch(model string) (routeDispatch, bool) {
	// Provider-pool catalog takes highest priority.
	if s.findProviderPoolEntry(model) != nil {
		return routeDispatch{
			SessionID:    s.resolveSessionID(),
			TimeoutSec:   s.resolveTimeoutSec(),
			ProviderPool: true,
		}, true
	}

	if route := s.resolveRoute(model); route != nil {
		adapter := route.Adapter
		if adapter == "" {
			adapter = s.resolveAdapter()
		}
		nodeRef := route.NodeRef
		if nodeRef == "" {
			nodeRef = s.cfg.NodeRef
		}
		sessionID := route.SessionID
		if sessionID == "" {
			sessionID = s.resolveSessionID()
		}
		timeoutSec := route.TimeoutSec
		if timeoutSec <= 0 {
			timeoutSec = s.resolveTimeoutSec()
		}
		return routeDispatch{
			NodeRef:           nodeRef,
			Adapter:           adapter,
			Target:            route.Target,
			SessionID:         sessionID,
			TimeoutSec:        timeoutSec,
			MaxQueue:          route.MaxQueue,
			QueueTimeoutMS:    route.QueueTimeoutMS,
			WorkspaceRequired: route.WorkspaceRequired,
		}, true
	}
	target := s.resolveTarget(model)
	if target == "" {
		return routeDispatch{}, false
	}
	return routeDispatch{
		NodeRef:    s.cfg.NodeRef,
		Adapter:    s.resolveAdapter(),
		Target:     target,
		SessionID:  s.resolveSessionID(),
		TimeoutSec: s.resolveTimeoutSec(),
	}, true
}

func (s *Server) resolveSessionID() string {
	if s.cfg.SessionID != "" {
		return s.cfg.SessionID
	}
	return edgeservice.DefaultSessionID
}

func (s *Server) resolveTimeoutSec() int {
	if s.cfg.TimeoutSec > 0 {
		return s.cfg.TimeoutSec
	}
	return edgeservice.DefaultTimeoutSec
}

func validateWorkspaceForRoute(d routeDispatch, workspace string) error {
	if !d.WorkspaceRequired {
		return nil
	}
	if strings.TrimSpace(workspace) == "" {
		return fmt.Errorf("workspace is required for this model route")
	}
	if !filepath.IsAbs(workspace) {
		return fmt.Errorf("workspace must be an absolute path")
	}
	return nil
}
