package sqlite

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"time"

	"github.com/golang-migrate/migrate/v4"
	"github.com/huandu/go-sqlbuilder"
	"github.com/sagernet/sing-box/common/byteformats"
	"github.com/sagernet/sing-box/service/manager/constant"
	_ "modernc.org/sqlite"
)

var squadFilters, nodeFilters, userFilters, bandwidthLimiterFilters, connectionLimiterFilters, trafficLimiterFilters, rateLimiterFilters map[string]Filter

type SQLiteRepository struct {
	db  *sql.DB
	ctx context.Context
}

func NewSQLiteRepository(ctx context.Context, dsn string) (*SQLiteRepository, error) {
	db, err := sql.Open("sqlite", dsn)
	if err != nil {
		return nil, err
	}
	if err := Migrate(db); err != nil && err != migrate.ErrNoChange {
		db.Close()
		return nil, err
	}
	return &SQLiteRepository{db: db, ctx: ctx}, nil
}

func (r *SQLiteRepository) CreateSquad(squad constant.SquadCreate) (constant.Squad, error) {
	var s constant.Squad
	now := time.Now()
	err := r.db.QueryRowContext(
		r.ctx, `
		INSERT INTO squads
		(
			name,
			created_at,
			updated_at
		)
		VALUES (?, ?, ?)
		RETURNING
			id,
			name,
			created_at,
			updated_at
	`,
		squad.Name,
		now,
		now,
	).Scan(
		&s.ID,
		&s.Name,
		&s.CreatedAt,
		&s.UpdatedAt,
	)
	return s, err
}

func (r *SQLiteRepository) GetSquads(filters map[string][]string) ([]constant.Squad, error) {
	sb := sqlbuilder.SQLite.NewSelectBuilder().
		Select(
			"id",
			"name",
			"created_at",
			"updated_at",
		).
		From("squads")
	for k, v := range filters {
		if f, ok := squadFilters[k]; ok {
			if err := f(sb, v); err != nil {
				return nil, err
			}
		}
	}
	query, args := sb.Build()
	rows, err := r.db.QueryContext(r.ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var result []constant.Squad
	for rows.Next() {
		var squad constant.Squad
		if err := rows.Scan(
			&squad.ID,
			&squad.Name,
			&squad.CreatedAt,
			&squad.UpdatedAt,
		); err != nil {
			return nil, err
		}
		result = append(result, squad)
	}
	return result, rows.Err()
}

func (r *SQLiteRepository) GetSquadsCount(filters map[string][]string) (int, error) {
	sb := sqlbuilder.SQLite.NewSelectBuilder().
		Select("COUNT(*)").
		From("squads")
	for k, v := range filters {
		if f, ok := squadFilters[k]; ok {
			if err := f(sb, v); err != nil {
				return 0, err
			}
		}
	}
	query, args := sb.Build()
	var count int
	err := r.db.QueryRowContext(r.ctx, query, args...).Scan(&count)
	return count, err
}

func (r *SQLiteRepository) GetSquad(id int) (constant.Squad, error) {
	var s constant.Squad
	err := r.db.QueryRowContext(r.ctx, `
		SELECT
			id,
			name,
			created_at,
			updated_at
		FROM squads
		WHERE id=?
	`, id).Scan(
		&s.ID,
		&s.Name,
		&s.CreatedAt,
		&s.UpdatedAt,
	)
	return s, err
}

func (r *SQLiteRepository) UpdateSquad(id int, squad constant.SquadUpdate) (constant.Squad, error) {
	var s constant.Squad
	err := r.db.QueryRowContext(
		r.ctx, `
		UPDATE squads
		SET
			name=?,
			updated_at=?
		WHERE id=?
		RETURNING
			id,
			name,
			created_at,
			updated_at
	`,
		squad.Name,
		time.Now(),
		id,
	).Scan(
		&s.ID,
		&s.Name,
		&s.CreatedAt,
		&s.UpdatedAt,
	)
	return s, err
}

func (r *SQLiteRepository) DeleteSquad(id int) (constant.DeletedSquad, error) {
	var result constant.DeletedSquad
	tx, err := r.db.BeginTx(r.ctx, nil)
	if err != nil {
		return result, err
	}
	defer tx.Rollback()
	affectedNodeRows, err := tx.QueryContext(r.ctx, `DELETE FROM node_to_squad WHERE squad_id=? RETURNING node_uuid`, id)
	if err != nil {
		return result, err
	}
	affectedNodeUUIDs := make([]string, 0)
	for affectedNodeRows.Next() {
		var uuid string
		if err = affectedNodeRows.Scan(&uuid); err != nil {
			affectedNodeRows.Close()
			return result, err
		}
		affectedNodeUUIDs = append(affectedNodeUUIDs, uuid)
	}
	affectedNodeRows.Close()
	if err = affectedNodeRows.Err(); err != nil {
		return result, err
	}
	for _, table := range []string{
		"user_to_squad",
		"connection_limiter_to_squad",
		"bandwidth_limiter_to_squad",
		"traffic_limiter_to_squad",
		"rate_limiter_to_squad",
	} {
		if _, err = tx.ExecContext(r.ctx, `DELETE FROM `+table+` WHERE squad_id=?`, id); err != nil {
			return result, err
		}
	}
	err = tx.QueryRowContext(r.ctx, `
		DELETE FROM squads
		WHERE id=?
		RETURNING
			id,
			name,
			created_at,
			updated_at
	`, id).Scan(
		&result.Squad.ID,
		&result.Squad.Name,
		&result.Squad.CreatedAt,
		&result.Squad.UpdatedAt,
	)
	if err != nil {
		return result, err
	}
	orphanedNodeRows, err := tx.QueryContext(r.ctx, `
		DELETE FROM nodes
		WHERE NOT EXISTS (SELECT 1 FROM node_to_squad WHERE node_to_squad.node_uuid = nodes.uuid)
		RETURNING uuid
	`)
	if err != nil {
		return result, err
	}
	orphanedNodeUUIDs := make(map[string]struct{})
	for orphanedNodeRows.Next() {
		var uuid string
		if err = orphanedNodeRows.Scan(&uuid); err != nil {
			orphanedNodeRows.Close()
			return result, err
		}
		orphanedNodeUUIDs[uuid] = struct{}{}
		result.OrphanedNodeUUIDs = append(result.OrphanedNodeUUIDs, uuid)
	}
	orphanedNodeRows.Close()
	if err = orphanedNodeRows.Err(); err != nil {
		return result, err
	}
	for _, uuid := range affectedNodeUUIDs {
		if _, ok := orphanedNodeUUIDs[uuid]; !ok {
			result.SurvivingNodeUUIDs = append(result.SurvivingNodeUUIDs, uuid)
		}
	}
	if _, err = tx.ExecContext(r.ctx, `
		DELETE FROM users
		WHERE NOT EXISTS (SELECT 1 FROM user_to_squad WHERE user_to_squad.user_id = users.id)
	`); err != nil {
		return result, err
	}
	connRows, err := tx.QueryContext(r.ctx, `
		DELETE FROM connection_limiters
		WHERE NOT EXISTS (SELECT 1 FROM connection_limiter_to_squad WHERE connection_limiter_to_squad.connection_limiter_id = connection_limiters.id)
		RETURNING id
	`)
	if err != nil {
		return result, err
	}
	for connRows.Next() {
		var lid int
		if err = connRows.Scan(&lid); err != nil {
			connRows.Close()
			return result, err
		}
		result.OrphanedConnectionLimiterIDs = append(result.OrphanedConnectionLimiterIDs, lid)
	}
	connRows.Close()
	if err = connRows.Err(); err != nil {
		return result, err
	}
	if _, err = tx.ExecContext(r.ctx, `
		DELETE FROM bandwidth_limiters
		WHERE NOT EXISTS (SELECT 1 FROM bandwidth_limiter_to_squad WHERE bandwidth_limiter_to_squad.bandwidth_limiter_id = bandwidth_limiters.id)
	`); err != nil {
		return result, err
	}
	trafficRows, err := tx.QueryContext(r.ctx, `
		DELETE FROM traffic_limiters
		WHERE NOT EXISTS (SELECT 1 FROM traffic_limiter_to_squad WHERE traffic_limiter_to_squad.traffic_limiter_id = traffic_limiters.id)
		RETURNING id
	`)
	if err != nil {
		return result, err
	}
	for trafficRows.Next() {
		var lid int
		if err = trafficRows.Scan(&lid); err != nil {
			trafficRows.Close()
			return result, err
		}
		result.OrphanedTrafficLimiterIDs = append(result.OrphanedTrafficLimiterIDs, lid)
	}
	trafficRows.Close()
	if err = trafficRows.Err(); err != nil {
		return result, err
	}
	if _, err = tx.ExecContext(r.ctx, `
		DELETE FROM rate_limiters
		WHERE NOT EXISTS (SELECT 1 FROM rate_limiter_to_squad WHERE rate_limiter_to_squad.rate_limiter_id = rate_limiters.id)
	`); err != nil {
		return result, err
	}
	if err = tx.Commit(); err != nil {
		return result, err
	}
	return result, nil
}

func (r *SQLiteRepository) CreateNode(node constant.NodeCreate) (constant.Node, error) {
	var n constant.Node
	tx, err := r.db.BeginTx(r.ctx, nil)
	if err != nil {
		return n, err
	}
	defer tx.Rollback()
	now := time.Now()
	err = tx.QueryRowContext(
		r.ctx, `
		INSERT INTO nodes (
			uuid,
			name,
			created_at,
			updated_at
		)
		VALUES (?, ?, ?, ?)
		RETURNING 
			uuid,
			name,
			created_at,
			updated_at
	`,
		node.UUID,
		node.Name,
		now,
		now,
	).Scan(
		&n.UUID,
		&n.Name,
		&n.CreatedAt,
		&n.UpdatedAt,
	)
	if err != nil {
		return n, err
	}
	stmt, err := tx.PrepareContext(r.ctx, `INSERT INTO node_to_squad (node_uuid, squad_id) VALUES (?, ?)`)
	if err != nil {
		return n, err
	}
	defer stmt.Close()
	for _, squadID := range node.SquadIDs {
		if _, err = stmt.ExecContext(r.ctx, node.UUID, squadID); err != nil {
			return n, err
		}
	}
	if err = tx.Commit(); err != nil {
		return n, err
	}
	n.SquadIDs = node.SquadIDs
	return n, nil
}

func (r *SQLiteRepository) GetNodes(filters map[string][]string) ([]constant.Node, error) {
	sb := sqlbuilder.SQLite.NewSelectBuilder().
		Select(
			"uuid",
			"name",
			`(
				SELECT json_group_array(squad_id)
				FROM node_to_squad
				WHERE node_to_squad.node_uuid = nodes.uuid
			) as squad_ids`,
			"created_at",
			"updated_at",
		).
		From("nodes")
	for key, value := range filters {
		if filter, ok := nodeFilters[key]; ok {
			if err := filter(sb, value); err != nil {
				return nil, err
			}
		}
	}
	query, args := sb.Build()
	rows, err := r.db.QueryContext(r.ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var result []constant.Node
	for rows.Next() {
		var n constant.Node
		var squadIDs intSliceJSON
		if err := rows.Scan(
			&n.UUID,
			&n.Name,
			&squadIDs,
			&n.CreatedAt,
			&n.UpdatedAt,
		); err != nil {
			return nil, err
		}
		n.SquadIDs = []int(squadIDs)
		result = append(result, n)
	}
	return result, rows.Err()
}

func (r *SQLiteRepository) GetNodesCount(filters map[string][]string) (int, error) {
	sb := sqlbuilder.SQLite.NewSelectBuilder().
		Select("COUNT(*)").
		From("nodes")
	for key, value := range filters {
		if filter, ok := nodeFilters[key]; ok {
			if err := filter(sb, value); err != nil {
				return 0, err
			}
		}
	}
	query, args := sb.Build()
	var count int
	err := r.db.QueryRowContext(r.ctx, query, args...).Scan(&count)
	return count, err
}

func (r *SQLiteRepository) GetNode(uuid string) (constant.Node, error) {
	var n constant.Node
	var squadIDs intSliceJSON
	err := r.db.QueryRowContext(r.ctx, `
		SELECT 
			uuid,
			name, 
			(
				SELECT json_group_array(squad_id)
				FROM node_to_squad
				WHERE node_to_squad.node_uuid = nodes.uuid
			) as squad_ids,
			created_at,
			updated_at
		FROM nodes
		WHERE uuid = ?
	`, uuid).Scan(
		&n.UUID,
		&n.Name,
		&squadIDs,
		&n.CreatedAt,
		&n.UpdatedAt,
	)
	if errors.Is(err, sql.ErrNoRows) {
		return n, constant.ErrNotFound
	}
	n.SquadIDs = []int(squadIDs)
	return n, err
}

func (r *SQLiteRepository) UpdateNode(uuid string, node constant.NodeUpdate) (constant.Node, error) {
	var n constant.Node
	err := r.db.QueryRowContext(
		r.ctx, `
		UPDATE nodes
		SET 
			name = ?,
			updated_at = ?
		WHERE uuid = ?
		RETURNING
			uuid,
			name,
			created_at,
			updated_at
	`,
		node.Name,
		time.Now(),
		uuid,
	).Scan(
		&n.UUID,
		&n.Name,
		&n.CreatedAt,
		&n.UpdatedAt,
	)
	return n, err
}

func (r *SQLiteRepository) DeleteNode(uuid string) (constant.Node, error) {
	var n constant.Node
	err := r.db.QueryRowContext(r.ctx, `
		DELETE FROM nodes
		WHERE uuid = ?
		RETURNING 
			uuid,
			name,
			created_at,
			updated_at
	`, uuid).Scan(
		&n.UUID,
		&n.Name,
		&n.CreatedAt,
		&n.UpdatedAt,
	)
	return n, err
}

func (r *SQLiteRepository) CreateUser(user constant.UserCreate) (constant.User, error) {
	var u constant.User
	tx, err := r.db.BeginTx(r.ctx, nil)
	if err != nil {
		return u, err
	}
	defer tx.Rollback()
	now := time.Now()
	err = tx.QueryRowContext(
		r.ctx, `
		INSERT INTO users (
			username,
			inbound,
			type,
			uuid,
			password,
			secret,
			flow,
			alter_id,
			created_at,
			updated_at
		)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
		RETURNING 
			id,
			username,
			inbound,
			type,
			uuid,
			password,
			secret,
			flow,
			alter_id,
			created_at,
			updated_at
	`,
		user.Username,
		user.Inbound,
		user.Type,
		user.UUID,
		user.Password,
		user.Secret,
		user.Flow,
		user.AlterID,
		now,
		now,
	).Scan(
		&u.ID,
		&u.Username,
		&u.Inbound,
		&u.Type,
		&u.UUID,
		&u.Password,
		&u.Secret,
		&u.Flow,
		&u.AlterID,
		&u.CreatedAt,
		&u.UpdatedAt,
	)
	if err != nil {
		return u, err
	}
	stmt, err := tx.PrepareContext(r.ctx, `INSERT INTO user_to_squad (user_id, squad_id) VALUES (?, ?)`)
	if err != nil {
		return u, err
	}
	defer stmt.Close()
	for _, squadID := range user.SquadIDs {
		if _, err = stmt.ExecContext(r.ctx, u.ID, squadID); err != nil {
			return u, err
		}
	}
	if err = tx.Commit(); err != nil {
		return u, err
	}
	u.SquadIDs = user.SquadIDs
	return u, nil
}

func (r *SQLiteRepository) GetUsers(filters map[string][]string) ([]constant.User, error) {
	sb := sqlbuilder.SQLite.NewSelectBuilder().
		Select(
			"id",
			`(
				SELECT json_group_array(squad_id)
				FROM user_to_squad
				WHERE user_to_squad.user_id = users.id
			) as squad_ids`,
			"username",
			"inbound",
			"type",
			"uuid",
			"password",
			"secret",
			"flow",
			"alter_id",
			"created_at",
			"updated_at",
		).
		From("users")
	for key, value := range filters {
		if filter, ok := userFilters[key]; ok {
			if err := filter(sb, value); err != nil {
				return nil, err
			}
		}
	}
	query, args := sb.Build()
	rows, err := r.db.QueryContext(r.ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var result []constant.User
	for rows.Next() {
		var u constant.User
		var squadIDs intSliceJSON
		if err := rows.Scan(
			&u.ID,
			&squadIDs,
			&u.Username,
			&u.Inbound,
			&u.Type,
			&u.UUID,
			&u.Password,
			&u.Secret,
			&u.Flow,
			&u.AlterID,
			&u.CreatedAt,
			&u.UpdatedAt,
		); err != nil {
			return nil, err
		}
		u.SquadIDs = []int(squadIDs)
		result = append(result, u)
	}
	return result, rows.Err()
}

func (r *SQLiteRepository) GetUsersCount(filters map[string][]string) (int, error) {
	sb := sqlbuilder.SQLite.NewSelectBuilder().
		Select("COUNT(*)").
		From("users")
	for key, value := range filters {
		if filter, ok := userFilters[key]; ok {
			if err := filter(sb, value); err != nil {
				return 0, err
			}
		}
	}
	query, args := sb.Build()
	var count int
	err := r.db.QueryRowContext(r.ctx, query, args...).Scan(&count)
	return count, err
}

func (r *SQLiteRepository) GetUser(id int) (constant.User, error) {
	var u constant.User
	var squadIDs intSliceJSON
	err := r.db.QueryRowContext(r.ctx, `
		SELECT
			id,
			(
				SELECT json_group_array(squad_id)
				FROM user_to_squad
				WHERE user_to_squad.user_id = users.id
			) as squad_ids,
			username,
			inbound,
			type,
			uuid,
			password,
			secret,
			flow,
			alter_id,
			created_at,
			updated_at
		FROM users
		WHERE id = ?
	`, id).Scan(
		&u.ID,
		&squadIDs,
		&u.Username,
		&u.Inbound,
		&u.Type,
		&u.UUID,
		&u.Password,
		&u.Secret,
		&u.Flow,
		&u.AlterID,
		&u.CreatedAt,
		&u.UpdatedAt,
	)
	u.SquadIDs = []int(squadIDs)
	return u, err
}

func (r *SQLiteRepository) UpdateUser(id int, user constant.UserUpdate) (constant.User, error) {
	var u constant.User
	var squadIDs intSliceJSON
	err := r.db.QueryRowContext(
		r.ctx, `
		UPDATE users
		SET
			uuid = ?,
			password = ?,
			secret = ?,
			flow = ?,
			alter_id = ?,
			updated_at = ?
		WHERE id = ?
		RETURNING
			id,
			(
				SELECT json_group_array(squad_id)
				FROM user_to_squad
				WHERE user_to_squad.user_id = users.id
			) as squad_ids,
			username,
			inbound,
			type,
			uuid,
			password,
			secret,
			flow,
			alter_id,
			created_at,
			updated_at
	`,
		user.UUID,
		user.Password,
		user.Secret,
		user.Flow,
		user.AlterID,
		time.Now(),
		id,
	).Scan(
		&u.ID,
		&squadIDs,
		&u.Username,
		&u.Inbound,
		&u.Type,
		&u.UUID,
		&u.Password,
		&u.Secret,
		&u.Flow,
		&u.AlterID,
		&u.CreatedAt,
		&u.UpdatedAt,
	)
	u.SquadIDs = []int(squadIDs)
	return u, err
}

func (r *SQLiteRepository) DeleteUser(id int) (constant.User, error) {
	var u constant.User
	var squadIDs intSliceJSON
	err := r.db.QueryRowContext(r.ctx, `
		DELETE FROM users
		WHERE id = ?
		RETURNING
			id,
			(
				SELECT json_group_array(squad_id)
				FROM user_to_squad
				WHERE user_to_squad.user_id = users.id
			) as squad_ids,
			username,
			inbound,
			type,
			uuid,
			password,
			secret,
			flow,
			alter_id,
			created_at,
			updated_at
	`, id).Scan(
		&u.ID,
		&squadIDs,
		&u.Username,
		&u.Inbound,
		&u.Type,
		&u.UUID,
		&u.Password,
		&u.Secret,
		&u.Flow,
		&u.AlterID,
		&u.CreatedAt,
		&u.UpdatedAt,
	)
	u.SquadIDs = []int(squadIDs)
	return u, err
}

func (r *SQLiteRepository) CreateConnectionLimiter(limiter constant.ConnectionLimiterCreate) (constant.ConnectionLimiter, error) {
	var cl constant.ConnectionLimiter
	tx, err := r.db.BeginTx(r.ctx, nil)
	if err != nil {
		return cl, err
	}
	defer tx.Rollback()
	now := time.Now()
	err = tx.QueryRowContext(
		r.ctx, `
		INSERT INTO connection_limiters
		(
			username,
			outbound,
			strategy,
			connection_type,
			lock_type,
			count,
			created_at,
			updated_at
		)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?)
		RETURNING
			id,
			username,
			outbound,
			strategy,
			connection_type,
			lock_type,
			count,
			created_at,
			updated_at
	`,
		limiter.Username,
		limiter.Outbound,
		limiter.Strategy,
		limiter.ConnectionType,
		limiter.LockType,
		limiter.Count,
		now,
		now,
	).Scan(
		&cl.ID,
		&cl.Username,
		&cl.Outbound,
		&cl.Strategy,
		&cl.ConnectionType,
		&cl.LockType,
		&cl.Count,
		&cl.CreatedAt,
		&cl.UpdatedAt,
	)
	if err != nil {
		return cl, err
	}
	stmt, err := tx.PrepareContext(r.ctx, `INSERT INTO connection_limiter_to_squad (connection_limiter_id, squad_id) VALUES (?, ?)`)
	if err != nil {
		return cl, err
	}
	defer stmt.Close()
	for _, squadID := range limiter.SquadIDs {
		if _, err = stmt.ExecContext(r.ctx, cl.ID, squadID); err != nil {
			return cl, err
		}
	}
	if err = tx.Commit(); err != nil {
		return cl, err
	}
	cl.SquadIDs = limiter.SquadIDs
	return cl, nil
}

func (r *SQLiteRepository) GetConnectionLimiters(filters map[string][]string) ([]constant.ConnectionLimiter, error) {
	sb := sqlbuilder.SQLite.NewSelectBuilder().
		Select(
			"id",
			`(
				SELECT json_group_array(squad_id)
				FROM connection_limiter_to_squad
				WHERE connection_limiter_to_squad.connection_limiter_id = connection_limiters.id
			) as squad_ids`,
			"username",
			"outbound",
			"strategy",
			"connection_type",
			"lock_type",
			"count",
			"created_at",
			"updated_at",
		).
		From("connection_limiters")
	for k, v := range filters {
		if f, ok := connectionLimiterFilters[k]; ok {
			if err := f(sb, v); err != nil {
				return nil, err
			}
		}
	}
	query, args := sb.Build()
	rows, err := r.db.QueryContext(r.ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var result []constant.ConnectionLimiter
	for rows.Next() {
		var cl constant.ConnectionLimiter
		var squadIDs intSliceJSON
		if err := rows.Scan(
			&cl.ID,
			&squadIDs,
			&cl.Username,
			&cl.Outbound,
			&cl.Strategy,
			&cl.ConnectionType,
			&cl.LockType,
			&cl.Count,
			&cl.CreatedAt,
			&cl.UpdatedAt,
		); err != nil {
			return nil, err
		}
		cl.SquadIDs = []int(squadIDs)
		result = append(result, cl)
	}

	return result, rows.Err()
}

func (r *SQLiteRepository) GetConnectionLimitersCount(filters map[string][]string) (int, error) {
	sb := sqlbuilder.SQLite.NewSelectBuilder().
		Select("COUNT(*)").
		From("connection_limiters")

	for k, v := range filters {
		if f, ok := connectionLimiterFilters[k]; ok {
			if err := f(sb, v); err != nil {
				return 0, err
			}
		}
	}
	query, args := sb.Build()
	var count int
	err := r.db.QueryRowContext(r.ctx, query, args...).Scan(&count)
	return count, err
}

func (r *SQLiteRepository) GetConnectionLimiter(id int) (constant.ConnectionLimiter, error) {
	var cl constant.ConnectionLimiter
	var squadIDs intSliceJSON
	err := r.db.QueryRowContext(r.ctx, `
		SELECT
			id,
			(
				SELECT json_group_array(squad_id)
				FROM connection_limiter_to_squad
				WHERE connection_limiter_to_squad.connection_limiter_id = connection_limiters.id
			) as squad_ids,
			username,
			outbound,
			strategy,
			connection_type,
			lock_type,
			count,
			created_at,
			updated_at
		FROM connection_limiters
		WHERE id=?
	`, id).Scan(
		&cl.ID,
		&squadIDs,
		&cl.Username,
		&cl.Outbound,
		&cl.Strategy,
		&cl.ConnectionType,
		&cl.LockType,
		&cl.Count,
		&cl.CreatedAt,
		&cl.UpdatedAt,
	)
	cl.SquadIDs = []int(squadIDs)
	return cl, err
}

func (r *SQLiteRepository) UpdateConnectionLimiter(id int, limiter constant.ConnectionLimiterUpdate) (constant.ConnectionLimiter, error) {
	var cl constant.ConnectionLimiter
	var squadIDs intSliceJSON
	err := r.db.QueryRowContext(
		r.ctx, `
		UPDATE connection_limiters
		SET
			strategy=?,
			connection_type=?,
			lock_type=?,
			count=?,
			updated_at=?
		WHERE id=?
		RETURNING
			id,
			(
				SELECT json_group_array(squad_id)
				FROM connection_limiter_to_squad
				WHERE connection_limiter_to_squad.connection_limiter_id = connection_limiters.id
			) as squad_ids,
			username,
			outbound,
			strategy,
			connection_type,
			lock_type,
			count,
			created_at,
			updated_at
	`,
		limiter.Strategy,
		limiter.ConnectionType,
		limiter.LockType,
		limiter.Count,
		time.Now(),
		id,
	).Scan(
		&cl.ID,
		&squadIDs,
		&cl.Username,
		&cl.Outbound,
		&cl.Strategy,
		&cl.ConnectionType,
		&cl.LockType,
		&cl.Count,
		&cl.CreatedAt,
		&cl.UpdatedAt,
	)
	cl.SquadIDs = []int(squadIDs)
	return cl, err
}

func (r *SQLiteRepository) DeleteConnectionLimiter(id int) (constant.ConnectionLimiter, error) {
	var cl constant.ConnectionLimiter
	var squadIDs intSliceJSON
	err := r.db.QueryRowContext(r.ctx, `
		DELETE FROM connection_limiters
		WHERE id=?
		RETURNING
			id,
			(
				SELECT json_group_array(squad_id)
				FROM connection_limiter_to_squad
				WHERE connection_limiter_to_squad.connection_limiter_id = connection_limiters.id
			) as squad_ids,
			username,
			outbound,
			strategy,
			connection_type,
			lock_type,
			count,
			created_at,
			updated_at
	`, id).Scan(
		&cl.ID,
		&squadIDs,
		&cl.Username,
		&cl.Outbound,
		&cl.Strategy,
		&cl.ConnectionType,
		&cl.LockType,
		&cl.Count,
		&cl.CreatedAt,
		&cl.UpdatedAt,
	)
	cl.SquadIDs = []int(squadIDs)
	return cl, err
}

func (r *SQLiteRepository) CreateBandwidthLimiter(limiter constant.BandwidthLimiterCreate) (constant.BandwidthLimiter, error) {
	var bl constant.BandwidthLimiter
	tx, err := r.db.BeginTx(r.ctx, nil)
	if err != nil {
		return bl, err
	}
	defer tx.Rollback()
	bytesSpeed, err := json.Marshal(limiter.Speed)
	if err != nil {
		return bl, err
	}
	raw := &byteformats.NetworkBytesCompat{}
	if err = raw.UnmarshalJSON(bytesSpeed); err != nil {
		return bl, err
	}
	flowKeysJSON, err := marshalStringSlice(limiter.FlowKeys)
	if err != nil {
		return bl, err
	}
	var flowKeys stringSliceJSON
	now := time.Now()
	err = tx.QueryRowContext(
		r.ctx, `
		INSERT INTO bandwidth_limiters
		(
			username,
			outbound,
			strategy,
			connection_type,
			mode,
			flow_keys,
			speed,
			raw_speed,
			created_at,
			updated_at
		)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
		RETURNING
			id,
			username,
			outbound,
			strategy,
			connection_type,
			mode,
			flow_keys,
			speed,
			raw_speed,
			created_at,
			updated_at
	`,
		limiter.Username,
		limiter.Outbound,
		limiter.Strategy,
		limiter.ConnectionType,
		limiter.Mode,
		flowKeysJSON,
		limiter.Speed,
		raw.Value(),
		now,
		now,
	).Scan(
		&bl.ID,
		&bl.Username,
		&bl.Outbound,
		&bl.Strategy,
		&bl.ConnectionType,
		&bl.Mode,
		&flowKeys,
		&bl.Speed,
		&bl.RawSpeed,
		&bl.CreatedAt,
		&bl.UpdatedAt,
	)
	if err != nil {
		return bl, err
	}
	bl.FlowKeys = []string(flowKeys)
	stmt, err := tx.PrepareContext(r.ctx, `INSERT INTO bandwidth_limiter_to_squad (bandwidth_limiter_id, squad_id) VALUES (?, ?)`)
	if err != nil {
		return bl, err
	}
	defer stmt.Close()
	for _, squadID := range limiter.SquadIDs {
		if _, err = stmt.ExecContext(r.ctx, bl.ID, squadID); err != nil {
			return bl, err
		}
	}
	if err = tx.Commit(); err != nil {
		return bl, err
	}
	bl.SquadIDs = limiter.SquadIDs
	return bl, nil
}

func (r *SQLiteRepository) GetBandwidthLimiters(filters map[string][]string) ([]constant.BandwidthLimiter, error) {
	sb := sqlbuilder.SQLite.NewSelectBuilder().
		Select(
			"id",
			`(
				SELECT json_group_array(squad_id)
				FROM bandwidth_limiter_to_squad
				WHERE bandwidth_limiter_to_squad.bandwidth_limiter_id = bandwidth_limiters.id
			) as squad_ids`,
			"username",
			"outbound",
			"strategy",
			"connection_type",
			"mode",
			"flow_keys",
			"speed",
			"raw_speed",
			"created_at",
			"updated_at",
		).
		From("bandwidth_limiters")

	for k, v := range filters {
		if f, ok := bandwidthLimiterFilters[k]; ok {
			if err := f(sb, v); err != nil {
				return nil, err
			}
		}
	}
	query, args := sb.Build()
	rows, err := r.db.QueryContext(r.ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var result []constant.BandwidthLimiter
	for rows.Next() {
		var bl constant.BandwidthLimiter
		var squadIDs intSliceJSON
		var flowKeys stringSliceJSON
		if err := rows.Scan(
			&bl.ID,
			&squadIDs,
			&bl.Username,
			&bl.Outbound,
			&bl.Strategy,
			&bl.ConnectionType,
			&bl.Mode,
			&flowKeys,
			&bl.Speed,
			&bl.RawSpeed,
			&bl.CreatedAt,
			&bl.UpdatedAt,
		); err != nil {
			return nil, err
		}
		bl.SquadIDs = []int(squadIDs)
		bl.FlowKeys = []string(flowKeys)
		result = append(result, bl)
	}
	return result, rows.Err()
}

func (r *SQLiteRepository) GetBandwidthLimitersCount(filters map[string][]string) (int, error) {
	sb := sqlbuilder.SQLite.NewSelectBuilder().
		Select("COUNT(*)").
		From("bandwidth_limiters")
	for k, v := range filters {
		if f, ok := bandwidthLimiterFilters[k]; ok {
			if err := f(sb, v); err != nil {
				return 0, err
			}
		}
	}
	query, args := sb.Build()
	var count int
	err := r.db.QueryRowContext(r.ctx, query, args...).Scan(&count)
	return count, err
}

func (r *SQLiteRepository) GetBandwidthLimiter(id int) (constant.BandwidthLimiter, error) {
	var bl constant.BandwidthLimiter
	var squadIDs intSliceJSON
	var flowKeys stringSliceJSON
	err := r.db.QueryRowContext(r.ctx, `
		SELECT
			id,
			(
				SELECT json_group_array(squad_id)
				FROM bandwidth_limiter_to_squad
				WHERE bandwidth_limiter_to_squad.bandwidth_limiter_id = bandwidth_limiters.id
			) as squad_ids,
			username,
			outbound,
			strategy,
			connection_type,
			mode,
			flow_keys,
			speed,
			raw_speed,
			created_at,
			updated_at
		FROM bandwidth_limiters
		WHERE id=?
	`, id).Scan(
		&bl.ID,
		&squadIDs,
		&bl.Username,
		&bl.Outbound,
		&bl.Strategy,
		&bl.ConnectionType,
		&bl.Mode,
		&flowKeys,
		&bl.Speed,
		&bl.RawSpeed,
		&bl.CreatedAt,
		&bl.UpdatedAt,
	)
	bl.SquadIDs = []int(squadIDs)
	bl.FlowKeys = []string(flowKeys)
	return bl, err
}

func (r *SQLiteRepository) UpdateBandwidthLimiter(id int, limiter constant.BandwidthLimiterUpdate) (constant.BandwidthLimiter, error) {
	var bl constant.BandwidthLimiter
	var squadIDs intSliceJSON
	var flowKeys stringSliceJSON
	bytesSpeed, err := json.Marshal(limiter.Speed)
	if err != nil {
		return bl, err
	}
	raw := &byteformats.NetworkBytesCompat{}
	if err = raw.UnmarshalJSON(bytesSpeed); err != nil {
		return bl, err
	}
	flowKeysJSON, err := marshalStringSlice(limiter.FlowKeys)
	if err != nil {
		return bl, err
	}
	err = r.db.QueryRowContext(
		r.ctx, `
		UPDATE bandwidth_limiters
		SET
			username=?,
			outbound=?,
			strategy=?,
			connection_type=?,
			mode=?,
			flow_keys=?,
			speed=?,
			raw_speed=?,
			updated_at=?
		WHERE id=?
		RETURNING
			id,
			(
				SELECT json_group_array(squad_id)
				FROM bandwidth_limiter_to_squad
				WHERE bandwidth_limiter_to_squad.bandwidth_limiter_id = bandwidth_limiters.id
			) as squad_ids,
			username,
			outbound,
			strategy,
			connection_type,
			mode,
			flow_keys,
			speed,
			raw_speed,
			created_at,
			updated_at
	`,
		limiter.Username,
		limiter.Outbound,
		limiter.Strategy,
		limiter.ConnectionType,
		limiter.Mode,
		flowKeysJSON,
		limiter.Speed,
		raw.Value(),
		time.Now(),
		id,
	).Scan(
		&bl.ID,
		&squadIDs,
		&bl.Username,
		&bl.Outbound,
		&bl.Strategy,
		&bl.ConnectionType,
		&bl.Mode,
		&flowKeys,
		&bl.Speed,
		&bl.RawSpeed,
		&bl.CreatedAt,
		&bl.UpdatedAt,
	)
	bl.SquadIDs = []int(squadIDs)
	bl.FlowKeys = []string(flowKeys)
	return bl, err
}

func (r *SQLiteRepository) DeleteBandwidthLimiter(id int) (constant.BandwidthLimiter, error) {
	var bl constant.BandwidthLimiter
	var squadIDs intSliceJSON
	var flowKeys stringSliceJSON
	err := r.db.QueryRowContext(r.ctx, `
		DELETE FROM bandwidth_limiters
		WHERE id=?
		RETURNING
			id,
			(
				SELECT json_group_array(squad_id)
				FROM bandwidth_limiter_to_squad
				WHERE bandwidth_limiter_to_squad.bandwidth_limiter_id = bandwidth_limiters.id
			) as squad_ids,
			username,
			outbound,
			strategy,
			connection_type,
			mode,
			flow_keys,
			speed,
			raw_speed,
			created_at,
			updated_at
	`, id).Scan(
		&bl.ID,
		&squadIDs,
		&bl.Username,
		&bl.Outbound,
		&bl.Strategy,
		&bl.ConnectionType,
		&bl.Mode,
		&flowKeys,
		&bl.Speed,
		&bl.RawSpeed,
		&bl.CreatedAt,
		&bl.UpdatedAt,
	)
	bl.SquadIDs = []int(squadIDs)
	bl.FlowKeys = []string(flowKeys)
	return bl, err
}

func (r *SQLiteRepository) CreateTrafficLimiter(limiter constant.TrafficLimiterCreate) (constant.TrafficLimiter, error) {
	var tl constant.TrafficLimiter
	tx, err := r.db.BeginTx(r.ctx, nil)
	if err != nil {
		return tl, err
	}
	defer tx.Rollback()
	bytesQuota, err := json.Marshal(limiter.Quota)
	if err != nil {
		return tl, err
	}
	rawQuota := &byteformats.NetworkBytesCompat{}
	if err = rawQuota.UnmarshalJSON(bytesQuota); err != nil {
		return tl, err
	}
	now := time.Now()
	err = tx.QueryRowContext(
		r.ctx, `
		INSERT INTO traffic_limiters
		(
			username,
			outbound,
			strategy,
			mode,
			quota,
			raw_quota,
			created_at,
			updated_at
		)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?)
		RETURNING
			id,
			username,
			outbound,
			strategy,
			mode,
			raw_used,
			quota,
			raw_quota,
			CASE WHEN raw_quota = 0 THEN 0 ELSE min(100, CAST(raw_used * 100.0 / raw_quota AS INTEGER)) END AS usage,
			created_at,
			updated_at
	`,
		limiter.Username,
		limiter.Outbound,
		limiter.Strategy,
		limiter.Mode,
		limiter.Quota,
		rawQuota.Value(),
		now,
		now,
	).Scan(
		&tl.ID,
		&tl.Username,
		&tl.Outbound,
		&tl.Strategy,
		&tl.Mode,
		&tl.RawUsed,
		&tl.Quota,
		&tl.RawQuota,
		&tl.Usage,
		&tl.CreatedAt,
		&tl.UpdatedAt,
	)
	if err != nil {
		return tl, err
	}
	stmt, err := tx.PrepareContext(r.ctx, `INSERT INTO traffic_limiter_to_squad (traffic_limiter_id, squad_id) VALUES (?, ?)`)
	if err != nil {
		return tl, err
	}
	defer stmt.Close()
	for _, squadID := range limiter.SquadIDs {
		if _, err = stmt.ExecContext(r.ctx, tl.ID, squadID); err != nil {
			return tl, err
		}
	}
	if err = tx.Commit(); err != nil {
		return tl, err
	}
	tl.SquadIDs = limiter.SquadIDs
	return tl, nil
}

func (r *SQLiteRepository) GetTrafficLimiters(filters map[string][]string) ([]constant.TrafficLimiter, error) {
	sb := sqlbuilder.SQLite.NewSelectBuilder().
		Select(
			"id",
			`(
				SELECT json_group_array(squad_id)
				FROM traffic_limiter_to_squad
				WHERE traffic_limiter_to_squad.traffic_limiter_id = traffic_limiters.id
			) as squad_ids`,
			"username",
			"outbound",
			"strategy",
			"mode",
			"raw_used",
			"quota",
			"raw_quota",
			"CASE WHEN raw_quota = 0 THEN 0 ELSE min(100, CAST(raw_used * 100.0 / raw_quota AS INTEGER)) END AS usage",
			"created_at",
			"updated_at",
		).
		From("traffic_limiters")

	for k, v := range filters {
		if f, ok := trafficLimiterFilters[k]; ok {
			if err := f(sb, v); err != nil {
				return nil, err
			}
		}
	}
	query, args := sb.Build()
	rows, err := r.db.QueryContext(r.ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var result []constant.TrafficLimiter
	for rows.Next() {
		var tl constant.TrafficLimiter
		var squadIDs intSliceJSON
		if err := rows.Scan(
			&tl.ID,
			&squadIDs,
			&tl.Username,
			&tl.Outbound,
			&tl.Strategy,
			&tl.Mode,
			&tl.RawUsed,
			&tl.Quota,
			&tl.RawQuota,
			&tl.Usage,
			&tl.CreatedAt,
			&tl.UpdatedAt,
		); err != nil {
			return nil, err
		}
		tl.SquadIDs = []int(squadIDs)
		result = append(result, tl)
	}
	return result, rows.Err()
}

func (r *SQLiteRepository) GetTrafficLimitersCount(filters map[string][]string) (int, error) {
	sb := sqlbuilder.SQLite.NewSelectBuilder().
		Select("COUNT(*)").
		From("traffic_limiters")
	for k, v := range filters {
		if f, ok := trafficLimiterFilters[k]; ok {
			if err := f(sb, v); err != nil {
				return 0, err
			}
		}
	}
	query, args := sb.Build()
	var count int
	err := r.db.QueryRowContext(r.ctx, query, args...).Scan(&count)
	return count, err
}

func (r *SQLiteRepository) GetTrafficLimiter(id int) (constant.TrafficLimiter, error) {
	var tl constant.TrafficLimiter
	var squadIDs intSliceJSON
	err := r.db.QueryRowContext(r.ctx, `
		SELECT
			id,
			(
				SELECT json_group_array(squad_id)
				FROM traffic_limiter_to_squad
				WHERE traffic_limiter_to_squad.traffic_limiter_id = traffic_limiters.id
			) as squad_ids,
			username,
			outbound,
			strategy,
			mode,
			raw_used,
			quota,
			raw_quota,
			CASE WHEN raw_quota = 0 THEN 0 ELSE min(100, CAST(raw_used * 100.0 / raw_quota AS INTEGER)) END AS usage,
			created_at,
			updated_at
		FROM traffic_limiters
		WHERE id=?
	`, id).Scan(
		&tl.ID,
		&squadIDs,
		&tl.Username,
		&tl.Outbound,
		&tl.Strategy,
		&tl.Mode,
		&tl.RawUsed,
		&tl.Quota,
		&tl.RawQuota,
		&tl.Usage,
		&tl.CreatedAt,
		&tl.UpdatedAt,
	)
	tl.SquadIDs = []int(squadIDs)
	return tl, err
}

func (r *SQLiteRepository) UpdateTrafficLimiter(id int, limiter constant.TrafficLimiterUpdate) (constant.TrafficLimiter, error) {
	var tl constant.TrafficLimiter
	var squadIDs intSliceJSON
	bytesQuota, err := json.Marshal(limiter.Quota)
	if err != nil {
		return tl, err
	}
	rawQuota := &byteformats.NetworkBytesCompat{}
	if err = rawQuota.UnmarshalJSON(bytesQuota); err != nil {
		return tl, err
	}
	err = r.db.QueryRowContext(
		r.ctx, `
		UPDATE traffic_limiters
		SET
			username=?,
			outbound=?,
			strategy=?,
			mode=?,
			quota=?,
			raw_quota=?,
			updated_at=?
		WHERE id=?
		RETURNING
			id,
			(
				SELECT json_group_array(squad_id)
				FROM traffic_limiter_to_squad
				WHERE traffic_limiter_to_squad.traffic_limiter_id = traffic_limiters.id
			) as squad_ids,
			username,
			outbound,
			strategy,
			mode,
			raw_used,
			quota,
			raw_quota,
			CASE WHEN raw_quota = 0 THEN 0 ELSE min(100, CAST(raw_used * 100.0 / raw_quota AS INTEGER)) END AS usage,
			created_at,
			updated_at
	`,
		limiter.Username,
		limiter.Outbound,
		limiter.Strategy,
		limiter.Mode,
		limiter.Quota,
		rawQuota.Value(),
		time.Now(),
		id,
	).Scan(
		&tl.ID,
		&squadIDs,
		&tl.Username,
		&tl.Outbound,
		&tl.Strategy,
		&tl.Mode,
		&tl.RawUsed,
		&tl.Quota,
		&tl.RawQuota,
		&tl.Usage,
		&tl.CreatedAt,
		&tl.UpdatedAt,
	)
	tl.SquadIDs = []int(squadIDs)
	return tl, err
}

func (r *SQLiteRepository) UpdateTrafficLimiterUsed(id int, current uint64) (constant.TrafficLimiter, error) {
	var tl constant.TrafficLimiter
	var squadIDs intSliceJSON
	err := r.db.QueryRowContext(
		r.ctx, `
		UPDATE traffic_limiters
		SET
			raw_used=?,
			updated_at=?
		WHERE id=?
		RETURNING
			id,
			(
				SELECT json_group_array(squad_id)
				FROM traffic_limiter_to_squad
				WHERE traffic_limiter_to_squad.traffic_limiter_id = traffic_limiters.id
			) as squad_ids,
			username,
			outbound,
			strategy,
			mode,
			raw_used,
			quota,
			raw_quota,
			CASE WHEN raw_quota = 0 THEN 0 ELSE min(100, CAST(raw_used * 100.0 / raw_quota AS INTEGER)) END AS usage,
			created_at,
			updated_at
	`,
		current,
		time.Now(),
		id,
	).Scan(
		&tl.ID,
		&squadIDs,
		&tl.Username,
		&tl.Outbound,
		&tl.Strategy,
		&tl.Mode,
		&tl.RawUsed,
		&tl.Quota,
		&tl.RawQuota,
		&tl.Usage,
		&tl.CreatedAt,
		&tl.UpdatedAt,
	)
	tl.SquadIDs = []int(squadIDs)
	return tl, err
}

func (r *SQLiteRepository) DeleteTrafficLimiter(id int) (constant.TrafficLimiter, error) {
	var tl constant.TrafficLimiter
	var squadIDs intSliceJSON
	err := r.db.QueryRowContext(r.ctx, `
		DELETE FROM traffic_limiters
		WHERE id=?
		RETURNING
			id,
			(
				SELECT json_group_array(squad_id)
				FROM traffic_limiter_to_squad
				WHERE traffic_limiter_to_squad.traffic_limiter_id = traffic_limiters.id
			) as squad_ids,
			username,
			outbound,
			strategy,
			mode,
			raw_used,
			quota,
			raw_quota,
			CASE WHEN raw_quota = 0 THEN 0 ELSE min(100, CAST(raw_used * 100.0 / raw_quota AS INTEGER)) END AS usage,
			created_at,
			updated_at
	`, id).Scan(
		&tl.ID,
		&squadIDs,
		&tl.Username,
		&tl.Outbound,
		&tl.Strategy,
		&tl.Mode,
		&tl.RawUsed,
		&tl.Quota,
		&tl.RawQuota,
		&tl.Usage,
		&tl.CreatedAt,
		&tl.UpdatedAt,
	)
	tl.SquadIDs = []int(squadIDs)
	return tl, err
}

func (r *SQLiteRepository) CreateRateLimiter(limiter constant.RateLimiterCreate) (constant.RateLimiter, error) {
	var rl constant.RateLimiter
	tx, err := r.db.BeginTx(r.ctx, nil)
	if err != nil {
		return rl, err
	}
	defer tx.Rollback()
	now := time.Now()
	err = tx.QueryRowContext(
		r.ctx, `
		INSERT INTO rate_limiters
		(
			username,
			outbound,
			strategy,
			connection_type,
			count,
			interval,
			created_at,
			updated_at
		)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?)
		RETURNING
			id,
			username,
			outbound,
			strategy,
			connection_type,
			count,
			interval,
			created_at,
			updated_at
	`,
		limiter.Username,
		limiter.Outbound,
		limiter.Strategy,
		limiter.ConnectionType,
		limiter.Count,
		limiter.Interval,
		now,
		now,
	).Scan(
		&rl.ID,
		&rl.Username,
		&rl.Outbound,
		&rl.Strategy,
		&rl.ConnectionType,
		&rl.Count,
		&rl.Interval,
		&rl.CreatedAt,
		&rl.UpdatedAt,
	)
	if err != nil {
		return rl, err
	}
	stmt, err := tx.PrepareContext(r.ctx, `INSERT INTO rate_limiter_to_squad (rate_limiter_id, squad_id) VALUES (?, ?)`)
	if err != nil {
		return rl, err
	}
	defer stmt.Close()
	for _, squadID := range limiter.SquadIDs {
		if _, err = stmt.ExecContext(r.ctx, rl.ID, squadID); err != nil {
			return rl, err
		}
	}
	if err = tx.Commit(); err != nil {
		return rl, err
	}
	rl.SquadIDs = limiter.SquadIDs
	return rl, nil
}

func (r *SQLiteRepository) GetRateLimiters(filters map[string][]string) ([]constant.RateLimiter, error) {
	sb := sqlbuilder.SQLite.NewSelectBuilder().
		Select(
			"id",
			`(
				SELECT json_group_array(squad_id)
				FROM rate_limiter_to_squad
				WHERE rate_limiter_to_squad.rate_limiter_id = rate_limiters.id
			) as squad_ids`,
			"username",
			"outbound",
			"strategy",
			"connection_type",
			"count",
			"interval",
			"created_at",
			"updated_at",
		).
		From("rate_limiters")

	for k, v := range filters {
		if f, ok := rateLimiterFilters[k]; ok {
			if err := f(sb, v); err != nil {
				return nil, err
			}
		}
	}
	query, args := sb.Build()
	rows, err := r.db.QueryContext(r.ctx, query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var result []constant.RateLimiter
	for rows.Next() {
		var rl constant.RateLimiter
		var squadIDs intSliceJSON
		if err := rows.Scan(
			&rl.ID,
			&squadIDs,
			&rl.Username,
			&rl.Outbound,
			&rl.Strategy,
			&rl.ConnectionType,
			&rl.Count,
			&rl.Interval,
			&rl.CreatedAt,
			&rl.UpdatedAt,
		); err != nil {
			return nil, err
		}
		rl.SquadIDs = []int(squadIDs)
		result = append(result, rl)
	}
	return result, rows.Err()
}

func (r *SQLiteRepository) GetRateLimitersCount(filters map[string][]string) (int, error) {
	sb := sqlbuilder.SQLite.NewSelectBuilder().
		Select("COUNT(*)").
		From("rate_limiters")
	for k, v := range filters {
		if f, ok := rateLimiterFilters[k]; ok {
			if err := f(sb, v); err != nil {
				return 0, err
			}
		}
	}
	query, args := sb.Build()
	var count int
	err := r.db.QueryRowContext(r.ctx, query, args...).Scan(&count)
	return count, err
}

func (r *SQLiteRepository) GetRateLimiter(id int) (constant.RateLimiter, error) {
	var rl constant.RateLimiter
	var squadIDs intSliceJSON
	err := r.db.QueryRowContext(r.ctx, `
		SELECT
			id,
			(
				SELECT json_group_array(squad_id)
				FROM rate_limiter_to_squad
				WHERE rate_limiter_to_squad.rate_limiter_id = rate_limiters.id
			) as squad_ids,
			username,
			outbound,
			strategy,
			connection_type,
			count,
			interval,
			created_at,
			updated_at
		FROM rate_limiters
		WHERE id=?
	`, id).Scan(
		&rl.ID,
		&squadIDs,
		&rl.Username,
		&rl.Outbound,
		&rl.Strategy,
		&rl.ConnectionType,
		&rl.Count,
		&rl.Interval,
		&rl.CreatedAt,
		&rl.UpdatedAt,
	)
	rl.SquadIDs = []int(squadIDs)
	return rl, err
}

func (r *SQLiteRepository) UpdateRateLimiter(id int, limiter constant.RateLimiterUpdate) (constant.RateLimiter, error) {
	var rl constant.RateLimiter
	var squadIDs intSliceJSON
	err := r.db.QueryRowContext(
		r.ctx, `
		UPDATE rate_limiters
		SET
			username=?,
			outbound=?,
			strategy=?,
			connection_type=?,
			count=?,
			interval=?,
			updated_at=?
		WHERE id=?
		RETURNING
			id,
			(
				SELECT json_group_array(squad_id)
				FROM rate_limiter_to_squad
				WHERE rate_limiter_to_squad.rate_limiter_id = rate_limiters.id
			) as squad_ids,
			username,
			outbound,
			strategy,
			connection_type,
			count,
			interval,
			created_at,
			updated_at
	`,
		limiter.Username,
		limiter.Outbound,
		limiter.Strategy,
		limiter.ConnectionType,
		limiter.Count,
		limiter.Interval,
		time.Now(),
		id,
	).Scan(
		&rl.ID,
		&squadIDs,
		&rl.Username,
		&rl.Outbound,
		&rl.Strategy,
		&rl.ConnectionType,
		&rl.Count,
		&rl.Interval,
		&rl.CreatedAt,
		&rl.UpdatedAt,
	)
	rl.SquadIDs = []int(squadIDs)
	return rl, err
}

func (r *SQLiteRepository) DeleteRateLimiter(id int) (constant.RateLimiter, error) {
	var rl constant.RateLimiter
	var squadIDs intSliceJSON
	err := r.db.QueryRowContext(r.ctx, `
		DELETE FROM rate_limiters
		WHERE id=?
		RETURNING
			id,
			(
				SELECT json_group_array(squad_id)
				FROM rate_limiter_to_squad
				WHERE rate_limiter_to_squad.rate_limiter_id = rate_limiters.id
			) as squad_ids,
			username,
			outbound,
			strategy,
			connection_type,
			count,
			interval,
			created_at,
			updated_at
	`, id).Scan(
		&rl.ID,
		&squadIDs,
		&rl.Username,
		&rl.Outbound,
		&rl.Strategy,
		&rl.ConnectionType,
		&rl.Count,
		&rl.Interval,
		&rl.CreatedAt,
		&rl.UpdatedAt,
	)
	rl.SquadIDs = []int(squadIDs)
	return rl, err
}

func init() {
	squadFilters = map[string]Filter{
		"id":               EqualFilter("id"),
		"id_in":            InFilter("id"),
		"pk":               EqualFilter("id"),
		"name":             EqualFilter("name"),
		"created_at_start": GreaterThanFilter("created_at"),
		"created_at_end":   LessThanFilter("created_at"),
		"updated_at_start": GreaterThanFilter("updated_at"),
		"updated_at_end":   LessThanFilter("updated_at"),
		"sort_asc":         SortAscFilter(),
		"sort_desc":        SortDescFilter(),
		"offset":           OffsetFilter(),
		"limit":            LimitFilter(),
	}
	nodeFilters = map[string]Filter{
		"uuid": EqualFilter("uuid"),
		"pk":   EqualFilter("uuid"),
		"name": EqualFilter("name"),
		"squad_id_in": ExistsAndWhereInFilter(
			sqlbuilder.SQLite.NewSelectBuilder().
				Select(
					"squad_id",
				).
				Where(
					"node_to_squad.node_uuid = nodes.uuid",
				).
				From(
					"node_to_squad",
				),
			"node_to_squad.squad_id",
		),
		"created_at_start": GreaterThanFilter("created_at"),
		"created_at_end":   LessThanFilter("created_at"),
		"updated_at_start": GreaterThanFilter("updated_at"),
		"updated_at_end":   LessThanFilter("updated_at"),
		"sort_asc":         SortAscFilter(),
		"sort_desc":        SortDescFilter(),
		"offset":           OffsetFilter(),
		"limit":            LimitFilter(),
	}
	userFilters = map[string]Filter{
		"id": EqualFilter("id"),
		"pk": EqualFilter("id"),
		"squad_id_in": ExistsAndWhereInFilter(
			sqlbuilder.SQLite.NewSelectBuilder().
				Select(
					"squad_id",
				).
				Where(
					"user_to_squad.user_id = users.id",
				).
				From(
					"user_to_squad",
				),
			"user_to_squad.squad_id",
		),
		"username":         EqualFilter("username"),
		"inbound":          EqualFilter("inbound"),
		"type":             EqualFilter("type"),
		"created_at_start": GreaterThanFilter("created_at"),
		"created_at_end":   LessThanFilter("created_at"),
		"updated_at_start": GreaterThanFilter("updated_at"),
		"updated_at_end":   LessThanFilter("updated_at"),
		"sort_asc":         SortAscFilter(),
		"sort_desc":        SortDescFilter(),
		"offset":           OffsetFilter(),
		"limit":            LimitFilter(),
	}
	connectionLimiterFilters = map[string]Filter{
		"id": EqualFilter("id"),
		"pk": EqualFilter("id"),
		"squad_id_in": ExistsAndWhereInFilter(
			sqlbuilder.SQLite.NewSelectBuilder().
				Select(
					"squad_id",
				).
				Where(
					"connection_limiter_to_squad.connection_limiter_id = connection_limiters.id",
				).
				From(
					"connection_limiter_to_squad",
				),
			"connection_limiter_to_squad.squad_id",
		),
		"strategy":         EqualFilter("strategy"),
		"username":         EqualFilter("username"),
		"outbound":         EqualFilter("outbound"),
		"connection_type":  EqualFilter("connection_type"),
		"lock_type":        EqualFilter("lock_type"),
		"created_at_start": GreaterThanFilter("created_at"),
		"created_at_end":   LessThanFilter("created_at"),
		"updated_at_start": GreaterThanFilter("updated_at"),
		"updated_at_end":   LessThanFilter("updated_at"),
		"sort_asc":         SortAscFilter(),
		"sort_desc":        SortDescFilter(),
		"offset":           OffsetFilter(),
		"limit":            LimitFilter(),
	}
	bandwidthLimiterFilters = map[string]Filter{
		"id": EqualFilter("id"),
		"pk": EqualFilter("id"),
		"squad_id_in": ExistsAndWhereInFilter(
			sqlbuilder.SQLite.NewSelectBuilder().
				Select(
					"squad_id",
				).
				Where(
					"bandwidth_limiter_to_squad.bandwidth_limiter_id = bandwidth_limiters.id",
				).
				From(
					"bandwidth_limiter_to_squad",
				),
			"bandwidth_limiter_to_squad.squad_id",
		),
		"strategy":         EqualFilter("strategy"),
		"mode":             EqualFilter("mode"),
		"type":             EqualFilter("type"),
		"username":         EqualFilter("username"),
		"down_start":       SpeedGreaterEqualThanFilter("raw_down"),
		"down_end":         SpeedLessEqualThanFilter("raw_down"),
		"up_start":         SpeedGreaterEqualThanFilter("raw_up"),
		"up_end":           SpeedLessEqualThanFilter("raw_up"),
		"created_at_start": GreaterThanFilter("created_at"),
		"created_at_end":   LessThanFilter("created_at"),
		"updated_at_start": GreaterThanFilter("updated_at"),
		"updated_at_end":   LessThanFilter("updated_at"),
		"sort_asc":         ReplacedSortAscFilter(map[string]string{"down": "raw_down", "up": "raw_up"}),
		"sort_desc":        ReplacedSortDescFilter(map[string]string{"down": "raw_down", "up": "raw_up"}),
		"offset":           OffsetFilter(),
		"limit":            LimitFilter(),
	}
	trafficLimiterFilters = map[string]Filter{
		"id": EqualFilter("id"),
		"pk": EqualFilter("id"),
		"squad_id_in": ExistsAndWhereInFilter(
			sqlbuilder.SQLite.NewSelectBuilder().
				Select(
					"squad_id",
				).
				Where(
					"traffic_limiter_to_squad.traffic_limiter_id = traffic_limiters.id",
				).
				From(
					"traffic_limiter_to_squad",
				),
			"traffic_limiter_to_squad.squad_id",
		),
		"username":         EqualFilter("username"),
		"outbound":         EqualFilter("outbound"),
		"strategy":         EqualFilter("strategy"),
		"mode":             EqualFilter("mode"),
		"used_start":    SpeedGreaterEqualThanFilter("raw_used"),
		"used_end":      SpeedLessEqualThanFilter("raw_used"),
		"quota_start":      SpeedGreaterEqualThanFilter("raw_quota"),
		"quota_end":        SpeedLessEqualThanFilter("raw_quota"),
		"created_at_start": GreaterThanFilter("created_at"),
		"created_at_end":   LessThanFilter("created_at"),
		"updated_at_start": GreaterThanFilter("updated_at"),
		"updated_at_end":   LessThanFilter("updated_at"),
		"sort_asc":         ReplacedSortAscFilter(map[string]string{"used": "raw_used", "quota": "raw_quota"}),
		"sort_desc":        ReplacedSortDescFilter(map[string]string{"used": "raw_used", "quota": "raw_quota"}),
		"offset":           OffsetFilter(),
		"limit":            LimitFilter(),
	}
	rateLimiterFilters = map[string]Filter{
		"id": EqualFilter("id"),
		"pk": EqualFilter("id"),
		"squad_id_in": ExistsAndWhereInFilter(
			sqlbuilder.SQLite.NewSelectBuilder().
				Select(
					"squad_id",
				).
				Where(
					"rate_limiter_to_squad.rate_limiter_id = rate_limiters.id",
				).
				From(
					"rate_limiter_to_squad",
				),
			"rate_limiter_to_squad.squad_id",
		),
		"strategy":         EqualFilter("strategy"),
		"username":         EqualFilter("username"),
		"outbound":         EqualFilter("outbound"),
		"connection_type":  EqualFilter("connection_type"),
		"interval":         EqualFilter("interval"),
		"count_start":      GreaterEqualThanFilter("count"),
		"count_end":        LessEqualThanFilter("count"),
		"created_at_start": GreaterThanFilter("created_at"),
		"created_at_end":   LessThanFilter("created_at"),
		"updated_at_start": GreaterThanFilter("updated_at"),
		"updated_at_end":   LessThanFilter("updated_at"),
		"sort_asc":         SortAscFilter(),
		"sort_desc":        SortDescFilter(),
		"offset":           OffsetFilter(),
		"limit":            LimitFilter(),
	}
}
