package services

import (
	"errors"
	"fmt"
	"strings"

	"clawreef/internal/models"
	"clawreef/internal/repository"
)

var orderedSystemImageTypes = []string{
	"openclaw",
	"ubuntu",
	"webtop",
	"hermes",
	"debian",
	"centos",
	"custom",
}

var supportedSystemImageTypes = map[string]string{
	"openclaw": "OpenClaw Pro",
	"ubuntu":   "Ubuntu Desktop",
	"webtop":   "Webtop Desktop",
	"hermes":   "Hermes Pro",
	"debian":   "Debian Desktop",
	"centos":   "CentOS Desktop",
	"custom":   "Custom Image",
}

var defaultSystemImageSettings = map[string]string{
	"openclaw": "ghcr.io/yuan-lab-llm/agentsruntime/openclaw:latest",
	"ubuntu":   "lscr.io/linuxserver/webtop:ubuntu-xfce",
	"webtop":   "lscr.io/linuxserver/webtop:ubuntu-xfce",
	"hermes":   "ghcr.io/yuan-lab-llm/agentsruntime/hermes:latest",
	"debian":   "docker.io/clawreef/debian-desktop:12",
	"centos":   "docker.io/clawreef/centos-desktop:9",
	"custom":   "registry.example.com/your-custom-image:latest",
}

var defaultGatewaySystemImageSettings = map[string]string{
	"openclaw": "ghcr.io/yuan-lab-llm/agentsruntime/openclaw-lite:latest",
	"ubuntu":   "ubuntu:22.04",
	"webtop":   "ubuntu:22.04",
	"hermes":   "ghcr.io/yuan-lab-llm/agentsruntime/hermes-lite:latest",
	"debian":   "debian:12",
	"centos":   "quay.io/centos/centos:stream9",
	"custom":   "registry.example.com/your-custom-shell-image:latest",
}

var defaultEnabledSystemImageTypes = map[string]bool{
	"openclaw": true,
	"ubuntu":   true,
	"hermes":   true,
}

var defaultEnabledGatewaySystemImageTypes = map[string]bool{
	"openclaw": true,
	"hermes":   true,
}

// RuntimeImageConfig is the runtime card selected for an instance type.
type RuntimeImageConfig struct {
	Image       string
	RuntimeType string
}

// RuntimeImageSettingsProvider exposes runtime image lookup for instance types.
type RuntimeImageSettingsProvider interface {
	GetRuntimeImage(instanceType string) (RuntimeImageConfig, bool)
	GetRuntimeImageForImage(instanceType, image string) (RuntimeImageConfig, bool)
}

var runtimeImageSettingsProvider RuntimeImageSettingsProvider

// SetRuntimeImageSettingsProvider configures the global runtime image provider used by runtime resolution.
func SetRuntimeImageSettingsProvider(provider RuntimeImageSettingsProvider) {
	runtimeImageSettingsProvider = provider
}

type SystemImageSettingService interface {
	List() ([]models.SystemImageSetting, error)
	Save(setting *models.SystemImageSetting) (*models.SystemImageSetting, error)
	DeleteByID(id int) error
	DisableType(instanceType string) error
	GetRuntimeImage(instanceType string) (RuntimeImageConfig, bool)
	GetRuntimeImageForImage(instanceType, image string) (RuntimeImageConfig, bool)
}

type systemImageSettingService struct {
	repo repository.SystemImageSettingRepository
}

// NewSystemImageSettingService creates a new system image setting service.
func NewSystemImageSettingService(repo repository.SystemImageSettingRepository) SystemImageSettingService {
	return &systemImageSettingService{repo: repo}
}

func (s *systemImageSettingService) List() ([]models.SystemImageSetting, error) {
	stored, err := s.repo.List()
	if err != nil {
		return nil, err
	}

	byType := make(map[string][]models.SystemImageSetting, len(orderedSystemImageTypes))
	for _, item := range stored {
		normalizedType := strings.TrimSpace(strings.ToLower(item.InstanceType))
		item.InstanceType = normalizedType
		item.RuntimeType = normalizeSystemImageRuntimeType(item.RuntimeType)
		if strings.TrimSpace(item.DisplayName) == "" {
			item.DisplayName = displayNameForSystemImageType(normalizedType)
		}
		byType[normalizedType] = append(byType[normalizedType], item)
	}

	settings := make([]models.SystemImageSetting, 0, len(stored)+len(orderedSystemImageTypes))
	for _, instanceType := range orderedSystemImageTypes {
		items := byType[instanceType]
		if len(items) == 0 {
			settings = append(settings, defaultSystemImagePresetsForType(instanceType)...)
			continue
		}

		for _, item := range items {
			if item.IsEnabled {
				settings = append(settings, item)
			}
		}
		delete(byType, instanceType)
	}

	for instanceType, items := range byType {
		for _, item := range items {
			if item.IsEnabled {
				if strings.TrimSpace(item.DisplayName) == "" {
					item.DisplayName = displayNameForSystemImageType(instanceType)
				}
				settings = append(settings, item)
			}
		}
	}

	return settings, nil
}

func (s *systemImageSettingService) Save(setting *models.SystemImageSetting) (*models.SystemImageSetting, error) {
	normalizedType := strings.TrimSpace(strings.ToLower(setting.InstanceType))
	if _, ok := supportedSystemImageTypes[normalizedType]; !ok {
		return nil, errors.New("unsupported instance type")
	}

	runtimeType, err := validateSystemImageRuntimeType(setting.RuntimeType)
	if err != nil {
		return nil, err
	}

	image := strings.TrimSpace(setting.Image)
	if image == "" {
		return nil, errors.New("image is required")
	}

	setting.InstanceType = normalizedType
	setting.RuntimeType = runtimeType
	setting.Image = image
	setting.DisplayName = strings.TrimSpace(setting.DisplayName)
	if setting.DisplayName == "" {
		setting.DisplayName = displayNameForSystemImagePreset(normalizedType, runtimeType)
	}
	setting.IsEnabled = true

	if err := s.repo.Save(setting); err != nil {
		return nil, err
	}

	return setting, nil
}

func (s *systemImageSettingService) DeleteByID(id int) error {
	if id <= 0 {
		return errors.New("invalid image setting id")
	}

	existing, err := s.repo.GetByID(id)
	if err != nil {
		return err
	}
	if existing == nil {
		return nil
	}

	if err := s.repo.DeleteByID(id); err != nil {
		return err
	}

	remaining, err := s.repo.ListByInstanceType(existing.InstanceType)
	if err != nil {
		return err
	}
	if len(remaining) > 0 || !isSupportedSystemImageType(existing.InstanceType) {
		return nil
	}

	return s.disableTypeWithFallback(existing.InstanceType)
}

func (s *systemImageSettingService) DisableType(instanceType string) error {
	normalizedType := strings.TrimSpace(strings.ToLower(instanceType))
	if !isSupportedSystemImageType(normalizedType) {
		return errors.New("unsupported instance type")
	}

	return s.disableTypeWithFallback(normalizedType)
}

func (s *systemImageSettingService) disableTypeWithFallback(instanceType string) error {
	if err := s.repo.DeleteByInstanceType(instanceType); err != nil {
		return err
	}

	return s.repo.Save(&models.SystemImageSetting{
		InstanceType: instanceType,
		RuntimeType:  "desktop",
		DisplayName:  displayNameForSystemImagePreset(instanceType, "desktop"),
		Image:        defaultSystemImageSettings[instanceType],
		IsEnabled:    false,
	})
}

func (s *systemImageSettingService) GetRuntimeImage(instanceType string) (RuntimeImageConfig, bool) {
	normalizedType := strings.TrimSpace(strings.ToLower(instanceType))
	items, err := s.repo.ListByInstanceType(normalizedType)
	if err != nil {
		return RuntimeImageConfig{}, false
	}

	if len(items) == 0 {
		for _, item := range defaultSystemImagePresetsForType(normalizedType) {
			image := strings.TrimSpace(item.Image)
			if item.IsEnabled && image != "" {
				return RuntimeImageConfig{Image: image, RuntimeType: item.RuntimeType}, true
			}
		}
		return RuntimeImageConfig{}, false
	}

	for _, item := range items {
		if !item.IsEnabled {
			continue
		}
		image := strings.TrimSpace(item.Image)
		if image != "" {
			return RuntimeImageConfig{
				Image:       image,
				RuntimeType: normalizeSystemImageRuntimeType(item.RuntimeType),
			}, true
		}
	}

	return RuntimeImageConfig{}, false
}

func (s *systemImageSettingService) GetRuntimeImageForImage(instanceType, image string) (RuntimeImageConfig, bool) {
	normalizedType := strings.TrimSpace(strings.ToLower(instanceType))
	normalizedImage := strings.TrimSpace(image)
	if normalizedType == "" || normalizedImage == "" {
		return RuntimeImageConfig{}, false
	}

	items, err := s.repo.ListByInstanceType(normalizedType)
	if err != nil {
		return RuntimeImageConfig{}, false
	}

	if len(items) == 0 {
		for _, item := range defaultSystemImagePresetsForType(normalizedType) {
			defaultImage := strings.TrimSpace(item.Image)
			if item.IsEnabled && defaultImage == normalizedImage {
				return RuntimeImageConfig{Image: defaultImage, RuntimeType: item.RuntimeType}, true
			}
		}
		return RuntimeImageConfig{}, false
	}

	for _, item := range items {
		if !item.IsEnabled {
			continue
		}
		if strings.TrimSpace(item.Image) == normalizedImage {
			return RuntimeImageConfig{
				Image:       normalizedImage,
				RuntimeType: normalizeSystemImageRuntimeType(item.RuntimeType),
			}, true
		}
	}

	return RuntimeImageConfig{}, false
}

func runtimeImageOverride(instanceType string) (RuntimeImageConfig, bool) {
	if runtimeImageSettingsProvider == nil {
		return RuntimeImageConfig{}, false
	}
	return runtimeImageSettingsProvider.GetRuntimeImage(instanceType)
}

func runtimeImageOverrideForImage(instanceType, image string) (RuntimeImageConfig, bool) {
	if runtimeImageSettingsProvider == nil {
		return RuntimeImageConfig{}, false
	}
	return runtimeImageSettingsProvider.GetRuntimeImageForImage(instanceType, image)
}

func displayNameForSystemImageType(instanceType string) string {
	if name, ok := supportedSystemImageTypes[instanceType]; ok {
		return name
	}
	return fmt.Sprintf("%s Image", instanceType)
}

func displayNameForSystemImagePreset(instanceType, runtimeType string) string {
	normalizedRuntimeType := normalizeSystemImageRuntimeType(runtimeType)
	if instanceType == "openclaw" {
		if normalizedRuntimeType == "gateway" {
			return "OpenClaw Lite"
		}
		return "OpenClaw Pro"
	}
	if instanceType == "hermes" {
		if normalizedRuntimeType == "gateway" {
			return "Hermes Lite"
		}
		return "Hermes Pro"
	}
	return displayNameForSystemImageType(instanceType)
}

func defaultSystemImagePresetsForType(instanceType string) []models.SystemImageSetting {
	settings := []models.SystemImageSetting{{
		InstanceType: instanceType,
		RuntimeType:  "desktop",
		DisplayName:  displayNameForSystemImagePreset(instanceType, "desktop"),
		Image:        defaultSystemImageSettings[instanceType],
		IsEnabled:    defaultEnabledSystemImageTypes[instanceType],
	}}

	if image := strings.TrimSpace(defaultGatewaySystemImageSettings[instanceType]); image != "" {
		if defaultEnabledGatewaySystemImageTypes[instanceType] {
			settings = append(settings, models.SystemImageSetting{
				InstanceType: instanceType,
				RuntimeType:  "gateway",
				DisplayName:  displayNameForSystemImagePreset(instanceType, "gateway"),
				Image:        image,
				IsEnabled:    true,
			})
		}
	}

	return settings
}

func isSupportedSystemImageType(instanceType string) bool {
	_, ok := supportedSystemImageTypes[instanceType]
	return ok
}

func normalizeSystemImageRuntimeType(runtimeType string) string {
	normalized := strings.TrimSpace(strings.ToLower(runtimeType))
	if normalized == "gateway" || normalized == "shell" {
		return "gateway"
	}
	return "desktop"
}

func validateSystemImageRuntimeType(runtimeType string) (string, error) {
	normalized := strings.TrimSpace(strings.ToLower(runtimeType))
	if normalized == "" {
		return "desktop", nil
	}
	if normalized == "shell" {
		return "gateway", nil
	}
	if normalized != "desktop" && normalized != "gateway" {
		return "", errors.New("unsupported runtime type")
	}
	return normalized, nil
}
