package data

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

	"kra/internal/conf"

	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
	"gorm.io/gorm"
)

const integrationKindWebSocket = "websocket"

// saveWebSocketIntegrationConfig persists the Melody settings in the shared
// integration table. It intentionally lives separately from storage/payment
// persistence so adding another transport does not expand their API surface.
func saveWebSocketIntegrationConfig(db *gorm.DB, config *conf.AdminBackend_WebSocket) error {
	if config == nil {
		config = &conf.AdminBackend_WebSocket{}
	}
	raw, err := protojson.MarshalOptions{UseProtoNames: true, EmitDefaultValues: true}.Marshal(config)
	if err != nil {
		return fmt.Errorf("encode websocket integration configuration: %w", err)
	}
	clean := db.Session(&gorm.Session{NewDB: true})
	var current integrationConfigPO
	err = clean.Where("kind = ? AND provider = ?", integrationKindWebSocket, "melody").First(&current).Error
	switch {
	case errors.Is(err, gorm.ErrRecordNotFound):
		return clean.Create(&integrationConfigPO{Kind: integrationKindWebSocket, Provider: "melody", Enabled: config.Enabled, Config: string(raw)}).Error
	case err != nil:
		return err
	default:
		return clean.Model(&current).Updates(map[string]any{"enabled": config.Enabled, "config": string(raw)}).Error
	}
}

func loadWebSocketIntegrationConfig(db *gorm.DB) (*conf.AdminBackend_WebSocket, bool, error) {
	var row integrationConfigPO
	err := db.Session(&gorm.Session{NewDB: true}).Where("kind = ? AND provider = ?", integrationKindWebSocket, "melody").First(&row).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, false, nil
	}
	if err != nil {
		return nil, false, err
	}
	if !json.Valid([]byte(row.Config)) {
		return nil, false, errors.New("invalid websocket integration configuration")
	}
	config := &conf.AdminBackend_WebSocket{}
	if err = (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal([]byte(row.Config), config); err != nil {
		return nil, false, fmt.Errorf("decode websocket integration configuration: %w", err)
	}
	config.Enabled = row.Enabled
	return config, true, nil
}

func resolveWebSocketIntegrationConfig(db *gorm.DB, legacy *conf.AdminBackend_WebSocket) (*conf.AdminBackend_WebSocket, error) {
	clean := db.Session(&gorm.Session{NewDB: true})
	if !clean.Migrator().HasTable(&integrationConfigPO{}) {
		if legacy == nil {
			return &conf.AdminBackend_WebSocket{}, nil
		}
		return proto.Clone(legacy).(*conf.AdminBackend_WebSocket), nil
	}
	config, found, err := loadWebSocketIntegrationConfig(clean)
	if err != nil {
		return nil, err
	}
	if found {
		return config, nil
	}
	if legacy == nil {
		legacy = &conf.AdminBackend_WebSocket{}
	}
	if err = saveWebSocketIntegrationConfig(clean, legacy); err != nil {
		return nil, err
	}
	config, _, err = loadWebSocketIntegrationConfig(clean)
	return config, err
}

func (d *Data) PersistWebSocketConfig(ctx context.Context, config *conf.AdminBackend_WebSocket) error {
	if !d.databaseReady.Load() {
		return errors.New("database is not initialized")
	}
	db := d.gormDB.WithContext(ctx)
	if !db.Migrator().HasTable(&integrationConfigPO{}) {
		return errors.New("integration configuration table does not exist")
	}
	return saveWebSocketIntegrationConfig(db, config)
}
