package subscribe

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

	"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 Subscribe struct {
	Id                int64     `gorm:"primaryKey"`
	Name              string    `gorm:"type:varchar(255);not null;default:'';comment:Subscribe Name"`
	Language          string    `gorm:"type:varchar(255);not null;default:'';comment:Language"`
	Description       string    `gorm:"type:text;comment:Subscribe Description"`
	UnitPrice         int64     `gorm:"type:int;not null;default:0;comment:Unit Price"`
	UnitTime          string    `gorm:"type:varchar(255);not null;default:'';comment:Unit Time"`
	Discount          string    `gorm:"type:text;comment:Discount"`
	Replacement       int64     `gorm:"type:int;not null;default:0;comment:Replacement"`
	Inventory         int64     `gorm:"type:int;not null;default:-1;comment:Inventory"`
	Traffic           int64     `gorm:"type:int;not null;default:0;comment:Traffic"`
	SpeedLimit        int64     `gorm:"type:int;not null;default:0;comment:Speed Limit"`
	DeviceLimit       int64     `gorm:"type:int;not null;default:0;comment:Device Limit"`
	Quota             int64     `gorm:"type:int;not null;default:0;comment:Quota"`
	NewUserOnly       *bool     `gorm:"type:tinyint(1);default:0;comment:New user only: allow purchase within 24h of registration, once per user"`
	Nodes             string         `gorm:"type:varchar(255);comment:Node Ids"`
	NodeTags          string         `gorm:"type:varchar(255);comment:Node Tags"`
	NodeGroupIds      JSONInt64Slice `gorm:"type:json;comment:Node Group IDs (JSON array, multiple groups)"`
	NodeGroupId       int64          `gorm:"default:0;index:idx_node_group_id;comment:Default Node Group ID (single ID)"`
	TrafficLimit      string         `gorm:"type:text;comment:Traffic Limit Rules"`
	Show              *bool          `gorm:"type:tinyint(1);not null;default:0;comment:Show portal page"`
	Sell              *bool     `gorm:"type:tinyint(1);not null;default:0;comment:Sell"`
	Sort              int64     `gorm:"type:int;not null;default:0;comment:Sort"`
	DeductionRatio    int64     `gorm:"type:int;default:0;comment:Deduction Ratio"`
	AllowDeduction    *bool     `gorm:"type:tinyint(1);default:1;comment:Allow deduction"`
	ResetCycle        int64     `gorm:"type:int;default:0;comment:Reset Cycle: 0: No Reset, 1: 1st, 2: Monthly, 3: Yearly"`
	RenewalReset      *bool     `gorm:"type:tinyint(1);default:0;comment:Renew Reset"`
	ShowOriginalPrice bool      `gorm:"type:tinyint(1);not null;default:1;comment:Show Original Price"`
	CreatedAt         time.Time `gorm:"<-:create;comment:Create Time"`
	UpdatedAt         time.Time `gorm:"comment:Update Time"`
}

func (*Subscribe) TableName() string {
	return "subscribe"
}

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

func (s *Subscribe) BeforeDelete(tx *gorm.DB) error {
	if err := tx.Exec("UPDATE `subscribe` SET sort = sort - 1 WHERE sort > ?", s.Sort).Error; err != nil {
		return err
	}
	return nil
}
func (s *Subscribe) BeforeUpdate(tx *gorm.DB) error {
	var count int64
	if err := tx.Set("gorm:query_option", "FOR UPDATE").Model(&Subscribe{}).
		Where("sort = ? AND id != ?", s.Sort, s.Id).Count(&count).Error; err != nil {
		return err
	}
	if count > 0 {
		var maxSort int64
		if err := tx.Model(&Subscribe{}).Select("MAX(sort)").Scan(&maxSort).Error; err != nil {
			return err
		}
		s.Sort = maxSort + 1
	}
	return nil
}

type Discount struct {
	Months      int64 `json:"months"`
	Discount    int64 `json:"discount"`
	NewUserOnly bool  `json:"new_user_only"`
}

type Group struct {
	Id          int64     `gorm:"primaryKey"`
	Name        string    `gorm:"type:varchar(255);not null;default:'';comment:Group Name"`
	Description string    `gorm:"type:text;comment:Group Description"`
	CreatedAt   time.Time `gorm:"<-:create;comment:Create Time"`
	UpdatedAt   time.Time `gorm:"comment:Update Time"`
}

func (Group) TableName() string {
	return "subscribe_group"
}
