package service

import (
	"encoding/json"
	"strconv"
	"strings"
	"time"
	"xboard-go/internal/database"
	"xboard-go/internal/model"

	"github.com/google/uuid"
)

const (
	PluginRealNameVerification = "real_name_verification"
	PluginUserOnlineDevices    = "user_online_devices"
	PluginUserAddIPv6          = "user_add_ipv6_subscription"
)

func GetPlugin(code string) (*model.Plugin, bool) {
	var plugin model.Plugin
	if err := database.DB.Where("code = ?", code).First(&plugin).Error; err != nil {
		return nil, false
	}
	return &plugin, true
}

func IsPluginEnabled(code string) bool {
	plugin, ok := GetPlugin(code)
	return ok && plugin.IsEnabled
}

func GetPluginConfig(code string) map[string]any {
	plugin, ok := GetPlugin(code)
	if !ok || plugin.Config == nil || *plugin.Config == "" {
		return map[string]any{}
	}

	var cfg map[string]any
	if err := json.Unmarshal([]byte(*plugin.Config), &cfg); err != nil {
		return map[string]any{}
	}
	return cfg
}

func GetPluginConfigString(code, key, defaultValue string) string {
	cfg := GetPluginConfig(code)
	value, ok := cfg[key]
	if !ok {
		return defaultValue
	}
	if raw, ok := value.(string); ok && raw != "" {
		return raw
	}
	return defaultValue
}

func GetPluginConfigBool(code, key string, defaultValue bool) bool {
	cfg := GetPluginConfig(code)
	value, ok := cfg[key]
	if !ok {
		return defaultValue
	}
	switch typed := value.(type) {
	case bool:
		return typed
	case float64:
		return typed != 0
	case string:
		return typed == "1" || typed == "true"
	default:
		return defaultValue
	}
}

func SyncIPv6ShadowAccount(user *model.User) bool {
	return SyncIPv6ShadowAccountWithPlan(user, 0)
}

// SyncIPv6ShadowAccountWithPlan creates or syncs an IPv6 shadow account.
// If overridePlanID > 0, it overrides the global ipv6_plan_id config for this user.
// This function no longer checks PluginUserAllowed — it is admin-only.
func SyncIPv6ShadowAccountWithPlan(user *model.User, overridePlanID int) bool {
	if user == nil {
		return false
	}

	ipv6Email := IPv6ShadowEmail(user.Email)
	var ipv6User model.User
	now := time.Now().Unix()
	created := false
	if err := database.DB.Where("email = ?", ipv6Email).First(&ipv6User).Error; err != nil {
		ipv6User = *user
		ipv6User.ID = 0
		ipv6User.Email = ipv6Email
		ipv6User.UUID = uuid.New().String()
		ipv6User.Token = GenerateSubscriptionToken()
		ipv6User.U = 0
		ipv6User.D = 0
		ipv6User.T = 0
		ipv6User.ParentID = &user.ID
		ipv6User.CreatedAt = now
		created = true
	}

	ipv6User.Email = ipv6Email
	ipv6User.Password = user.Password
	ipv6User.ParentID = &user.ID
	ipv6User.U = 0
	ipv6User.D = 0
	ipv6User.T = 0
	ipv6User.Banned = false
	ipv6User.UpdatedAt = now

	// Determine which plan to assign: override > global config
	if overridePlanID > 0 {
		ipv6User.PlanID = &overridePlanID
	} else if planID := parsePluginPositiveInt(GetPluginConfigString(PluginUserAddIPv6, "ipv6_plan_id", "0"), 0); planID > 0 {
		ipv6User.PlanID = &planID
	} else if ipv6User.PlanID == nil && user.PlanID != nil {
		ipv6User.PlanID = user.PlanID
	}
	if groupID := parsePluginPositiveInt(GetPluginConfigString(PluginUserAddIPv6, "ipv6_group_id", "0"), 0); groupID > 0 {
		ipv6User.GroupID = &groupID
	}

	if err := database.DB.Save(&ipv6User).Error; err != nil {
		syncIPv6SubscriptionRecord(user, nil, true, "eligible")
		return false
	}

	if created {
		_ = database.DB.Model(&model.User{}).Where("id = ?", ipv6User.ID).Update("parent_id", user.ID).Error
	}
	syncIPv6SubscriptionRecord(user, &ipv6User, true, "active")
	return true
}

// DisableIPv6ShadowAccount soft-disables the IPv6 shadow account for a user.
// It bans the shadow user and marks the subscription as disabled.
func DisableIPv6ShadowAccount(user *model.User) bool {
	if user == nil {
		return false
	}

	ipv6Email := IPv6ShadowEmail(user.Email)
	var ipv6User model.User

	var subscription model.UserIPv6Subscription
	if err := database.DB.Where("user_id = ?", user.ID).First(&subscription).Error; err == nil && subscription.ShadowUserID != nil {
		_ = database.DB.First(&ipv6User, *subscription.ShadowUserID).Error
	}
	if ipv6User.ID == 0 {
		_ = database.DB.Where("email = ?", ipv6Email).First(&ipv6User).Error
	}
	if ipv6User.ID == 0 {
		// No shadow user found, just update subscription record
		syncIPv6SubscriptionRecord(user, nil, false, "disabled")
		return true
	}

	// Ban the shadow user (soft disable)
	now := time.Now().Unix()
	_ = database.DB.Model(&model.User{}).Where("id = ?", ipv6User.ID).Updates(map[string]any{
		"banned":     true,
		"updated_at": now,
	}).Error

	syncIPv6SubscriptionRecord(user, &ipv6User, false, "disabled")
	return true
}

func PluginPlanAllowed(plan *model.Plan) bool {
	return PluginUserAllowed(nil, plan)
}

func PluginUserAllowed(user *model.User, plan *model.Plan) bool {
	allowedPlans := GetPluginConfigIntList(PluginUserAddIPv6, "allowed_plans")
	for _, planID := range allowedPlans {
		if plan != nil && plan.ID == planID {
			return true
		}
	}

	allowedGroups := GetPluginConfigIntList(PluginUserAddIPv6, "allowed_groups")
	for _, groupID := range allowedGroups {
		if user != nil && user.GroupID != nil && *user.GroupID == groupID {
			return true
		}
	}

	if plan == nil {
		return false
	}

	referenceFlag := strings.ToLower(GetPluginConfigString(PluginUserAddIPv6, "reference_flag", "ipv6"))
	reference := ""
	if plan.Reference != nil {
		reference = strings.ToLower(*plan.Reference)
	}
	return referenceFlag != "" && strings.Contains(reference, referenceFlag)
}

func GetPluginConfigIntList(code, key string) []int {
	cfg := GetPluginConfig(code)
	value, ok := cfg[key]
	if !ok || value == nil {
		return []int{}
	}

	parseItem := func(raw any) int {
		switch typed := raw.(type) {
		case int:
			if typed > 0 {
				return typed
			}
		case int64:
			if typed > 0 {
				return int(typed)
			}
		case float64:
			if typed > 0 {
				return int(typed)
			}
		case string:
			return parsePluginPositiveInt(typed, 0)
		}
		return 0
	}

	result := make([]int, 0)
	seen := make(map[int]struct{})
	appendValue := func(candidate int) {
		if candidate <= 0 {
			return
		}
		if _, ok := seen[candidate]; ok {
			return
		}
		seen[candidate] = struct{}{}
		result = append(result, candidate)
	}

	switch typed := value.(type) {
	case []any:
		for _, item := range typed {
			appendValue(parseItem(item))
		}
	case []int:
		for _, item := range typed {
			appendValue(item)
		}
	case []float64:
		for _, item := range typed {
			appendValue(int(item))
		}
	case string:
		for _, raw := range strings.Split(typed, ",") {
			appendValue(parsePluginPositiveInt(strings.TrimSpace(raw), 0))
		}
	}

	return result
}

func IPv6ShadowEmail(email string) string {
	suffix := GetPluginConfigString(PluginUserAddIPv6, "email_suffix", "-ipv6")
	parts := strings.SplitN(email, "@", 2)
	if len(parts) != 2 {
		return email
	}
	return parts[0] + suffix + "@" + parts[1]
}

func parsePluginPositiveInt(raw string, defaultValue int) int {
	value, err := strconv.Atoi(strings.TrimSpace(raw))
	if err != nil || value <= 0 {
		return defaultValue
	}
	return value
}

func SyncIPv6PasswordState(user *model.User) bool {
	if user == nil {
		return false
	}

	ipv6Email := IPv6ShadowEmail(user.Email)
	result := database.DB.Model(&model.User{}).Where("email = ?", ipv6Email).Update("password", user.Password)
	if result.Error != nil || result.RowsAffected == 0 {
		return false
	}

	now := time.Now().Unix()
	updates := map[string]any{
		"password_synced_at": now,
		"updated_at":         now,
	}

	var subscription model.UserIPv6Subscription
	if err := database.DB.Where("user_id = ?", user.ID).First(&subscription).Error; err == nil {
		_ = database.DB.Model(&model.UserIPv6Subscription{}).Where("id = ?", subscription.ID).Updates(updates).Error
	}
	return true
}

func syncIPv6SubscriptionRecord(user *model.User, shadowUser *model.User, allowed bool, status string) {
	if user == nil {
		return
	}

	now := time.Now().Unix()
	record := model.UserIPv6Subscription{
		UserID:     user.ID,
		IPv6Email:  IPv6ShadowEmail(user.Email),
		Allowed:    allowed,
		Status:     status,
		LastSyncAt: now,
		CreatedAt:  now,
		UpdatedAt:  now,
	}
	if shadowUser != nil {
		record.ShadowUserID = &shadowUser.ID
	}

	var existing model.UserIPv6Subscription
	if err := database.DB.Where("user_id = ?", user.ID).First(&existing).Error; err == nil {
		updates := map[string]any{
			"ipv6_email":   record.IPv6Email,
			"allowed":      allowed,
			"status":       status,
			"last_sync_at": now,
			"updated_at":   now,
		}
		if shadowUser != nil {
			updates["shadow_user_id"] = shadowUser.ID
		} else {
			updates["shadow_user_id"] = nil
		}
		_ = database.DB.Model(&model.UserIPv6Subscription{}).Where("id = ?", existing.ID).Updates(updates).Error
		return
	}

	_ = database.DB.Create(&record).Error
}
