package integration

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"

	"kra/internal/config"

	"gorm.io/gorm"
)

const (
	kindStorage  = "storage"
	kindEmail    = "email"
	providerSMTP = "smtp"
)

var storageProviders = []string{
	"local", "qiniu", "aliyun-oss", "huawei-obs", "tencent-cos", "aws-s3", "cloudflare-r2", "minio",
}

func normalizeStorageType(value string) string {
	value = strings.ToLower(strings.TrimSpace(value))
	if value == "" {
		return "local"
	}
	return value
}

func storageProviderValue(storage *config.Storage, provider string) any {
	if storage == nil {
		storage = &config.Storage{}
	}
	switch provider {
	case "qiniu":
		if storage.Qiniu == nil {
			storage.Qiniu = &config.Qiniu{}
		}
		return storage.Qiniu
	case "aliyun-oss":
		return ensureObjectStore(&storage.AliyunOSS)
	case "huawei-obs":
		return ensureObjectStore(&storage.HuaweiOBS)
	case "tencent-cos":
		return ensureObjectStore(&storage.TencentCOS)
	case "aws-s3":
		return ensureObjectStore(&storage.AWSS3)
	case "cloudflare-r2":
		return ensureObjectStore(&storage.CloudflareR2)
	case "minio":
		return ensureObjectStore(&storage.Minio)
	default:
		return nil
	}
}

func ensureObjectStore(value **config.ObjectStore) *config.ObjectStore {
	if *value == nil {
		*value = &config.ObjectStore{}
	}
	return *value
}

func marshalStorageProvider(storage *config.Storage, provider string) (string, error) {
	value := storageProviderValue(storage, provider)
	if value == nil {
		return "{}", nil
	}
	raw, err := json.Marshal(value)
	if err != nil {
		return "", err
	}
	return string(raw), nil
}

func unmarshalStorageProvider(storage *config.Storage, provider, value string) error {
	if provider == "local" || strings.TrimSpace(value) == "" {
		return nil
	}
	target := storageProviderValue(storage, provider)
	if target == nil {
		return nil
	}
	if !json.Valid([]byte(value)) {
		return fmt.Errorf("invalid %s integration configuration", provider)
	}
	if err := json.Unmarshal([]byte(value), target); err != nil {
		return fmt.Errorf("decode %s integration configuration: %w", provider, err)
	}
	return nil
}

func SaveStorageConfig(db *gorm.DB, storage *config.Storage) error {
	if storage == nil {
		storage = &config.Storage{}
	}
	active := normalizeStorageType(storage.Type)
	known := false
	for _, provider := range storageProviders {
		if provider == active {
			known = true
			break
		}
	}
	if !known {
		return fmt.Errorf("unsupported storage type %q", active)
	}

	rows := make([]ConfigRow, 0, len(storageProviders))
	for _, provider := range storageProviders {
		value, err := marshalStorageProvider(storage, provider)
		if err != nil {
			return fmt.Errorf("encode %s integration configuration: %w", provider, err)
		}
		rows = append(rows, ConfigRow{Provider: provider, Enabled: provider == active, Config: value})
	}
	return UpsertConfigs(db, kindStorage, rows)
}

func LoadStorageConfig(db *gorm.DB) (*config.Storage, bool, error) {
	rows, err := ListConfigs(db, kindStorage)
	if err != nil {
		return nil, false, err
	}
	if len(rows) == 0 {
		return nil, false, nil
	}
	storage := &config.Storage{Type: "local"}
	for _, row := range rows {
		if err = unmarshalStorageProvider(storage, row.Provider, row.Config); err != nil {
			return nil, false, err
		}
		if row.Enabled {
			storage.Type = row.Provider
		}
	}
	return storage, true, nil
}

// ResolveStorageConfig imports a legacy YAML value only when the database has
// no storage configuration yet. The database is authoritative afterwards.
func ResolveStorageConfig(db *gorm.DB, legacy *config.Storage) (*config.Storage, error) {
	if !HasConfigTable(db) {
		if legacy == nil {
			return &config.Storage{Type: "local"}, nil
		}
		return config.CloneStorage(legacy), nil
	}
	storage, found, err := LoadStorageConfig(db)
	if err != nil {
		return nil, err
	}
	if found {
		return storage, nil
	}
	if legacy == nil {
		legacy = &config.Storage{Type: "local"}
	}
	if err = SaveStorageConfig(db, legacy); err != nil {
		return nil, err
	}
	storage, _, err = LoadStorageConfig(db)
	return storage, err
}

func defaultEmailConfig() *config.Email { return &config.Email{Port: 465, IsSSL: true} }

func SaveEmailConfig(db *gorm.DB, email *config.Email) error {
	if email == nil {
		email = defaultEmailConfig()
	}
	raw, err := json.Marshal(email)
	if err != nil {
		return fmt.Errorf("encode smtp integration configuration: %w", err)
	}
	enabled := email.Host != "" && email.From != "" && email.Secret != "" && email.Port > 0
	return UpsertConfigs(db, kindEmail, []ConfigRow{{Provider: providerSMTP, Enabled: enabled, Config: string(raw)}})
}

func LoadEmailConfig(db *gorm.DB) (*config.Email, bool, error) {
	row, found, err := FindConfig(db, kindEmail, providerSMTP)
	if err != nil || !found {
		return nil, false, err
	}
	if !json.Valid([]byte(row.Config)) {
		return nil, false, errors.New("invalid smtp integration configuration")
	}
	email := defaultEmailConfig()
	if err = json.Unmarshal([]byte(row.Config), email); err != nil {
		return nil, false, fmt.Errorf("decode smtp integration configuration: %w", err)
	}
	return email, true, nil
}

func ResolveEmailConfig(db *gorm.DB, legacy *config.Email) (*config.Email, error) {
	if !HasConfigTable(db) {
		if legacy == nil {
			return defaultEmailConfig(), nil
		}
		return config.CloneEmail(legacy), nil
	}
	email, found, err := LoadEmailConfig(db)
	if err != nil {
		return nil, err
	}
	if found {
		return email, nil
	}
	if legacy == nil {
		legacy = defaultEmailConfig()
	}
	if err = SaveEmailConfig(db, legacy); err != nil {
		return nil, err
	}
	email, _, err = LoadEmailConfig(db)
	return email, err
}
