package node

import (
	"database/sql/driver"
	"encoding/json"
	"time"

	"github.com/perfect-panel/server/pkg/logger"
	"gorm.io/gorm"
)

// JSONInt64Slice is a custom type for handling []int64 as JSON in database
type JSONInt64Slice []int64

// Scan implements sql.Scanner interface
func (j *JSONInt64Slice) Scan(value interface{}) error {
	if value == nil {
		*j = []int64{}
		return nil
	}

	// Handle []byte
	bytes, ok := value.([]byte)
	if !ok {
		// Try to handle string
		str, ok := value.(string)
		if !ok {
			*j = []int64{}
			return nil
		}
		bytes = []byte(str)
	}

	if len(bytes) == 0 {
		*j = []int64{}
		return nil
	}

	// Check if it's a JSON array
	if bytes[0] != '[' {
		// Not a JSON array, return empty slice
		*j = []int64{}
		return nil
	}

	return json.Unmarshal(bytes, j)
}

// Value implements driver.Valuer interface
func (j JSONInt64Slice) Value() (driver.Value, error) {
	if len(j) == 0 {
		return "[]", nil
	}
	return json.Marshal(j)
}

type Node struct {
	Id           int64          `gorm:"primary_key"`
	Name         string         `gorm:"type:varchar(100);not null;default:'';comment:Node Name"`
	Tags         string         `gorm:"type:varchar(255);not null;default:'';comment:Tags"`
	Port         uint16         `gorm:"not null;default:0;comment:Connect Port"`
	Address      string         `gorm:"type:varchar(255);not null;default:'';comment:Connect Address"`
	ServerId     int64          `gorm:"not null;default:0;comment:Server ID"`
	Server       *Server        `gorm:"foreignKey:ServerId;references:Id"`
	Protocol     string         `gorm:"type:varchar(100);not null;default:'';comment:Protocol"`
	Enabled      *bool          `gorm:"type:boolean;not null;default:true;comment:Enabled"`
	Sort         int            `gorm:"uniqueIndex;not null;default:0;comment:Sort"`
	NodeGroupIds JSONInt64Slice `gorm:"type:json;comment:Node Group IDs (JSON array, multiple groups)"`
	CreatedAt    time.Time      `gorm:"<-:create;comment:Creation Time"`
	UpdatedAt    time.Time      `gorm:"comment:Update Time"`
}

func (n *Node) TableName() string {
	return "nodes"
}

func (n *Node) BeforeCreate(tx *gorm.DB) error {
	if n.Sort == 0 {
		var maxSort int
		if err := tx.Model(&Node{}).Select("COALESCE(MAX(sort), 0)").Scan(&maxSort).Error; err != nil {
			return err
		}
		n.Sort = maxSort + 1
	}
	return nil
}

func (n *Node) BeforeDelete(tx *gorm.DB) error {
	if err := tx.Exec("UPDATE `nodes` SET sort = sort - 1 WHERE sort > ?", n.Sort).Error; err != nil {
		return err
	}
	return nil
}

func (n *Node) BeforeUpdate(tx *gorm.DB) error {
	var count int64
	if err := tx.Set("gorm:query_option", "FOR UPDATE").Model(&Server{}).
		Where("sort = ? AND id != ?", n.Sort, n.Id).Count(&count).Error; err != nil {
		return err
	}
	if count > 1 {
		// reorder sort
		if err := reorderSortWithNode(tx); err != nil {
			logger.Errorf("[Server] BeforeUpdate reorderSort error: %v", err.Error())
			return err
		}
		// get max sort
		var maxSort int
		if err := tx.Model(&Server{}).Select("MAX(sort)").Scan(&maxSort).Error; err != nil {
			return err
		}
		n.Sort = maxSort + 1
	}
	return nil
}

func reorderSortWithNode(tx *gorm.DB) error {
	var nodes []Node
	if err := tx.Order("sort, id").Find(&nodes).Error; err != nil {
		return err
	}
	for i, node := range nodes {
		if node.Sort != i+1 {
			if err := tx.Exec("UPDATE `nodes` SET sort = ? WHERE id = ?", i+1, node.Id).Error; err != nil {
				return err
			}
		}
	}
	return nil
}
