package linedb

import (
	"time"
)

// LineDbAdapter представляет базовый интерфейс для записей в базе данных
// Соответствует TypeScript интерфейсу LineDbAdapter
type LineDbAdapter interface {
	GetID() any
	SetID(id any)
	GetTimestamp() *time.Time
	SetTimestamp(timestamp *time.Time)
}

// BaseRecord представляет базовую структуру записи
type BaseRecord struct {
	ID        any        `json:"id"`
	Timestamp *time.Time `json:"timestamp,omitempty"`
}

func (r *BaseRecord) GetID() any {
	return r.ID
}

func (r *BaseRecord) SetID(id any) {
	r.ID = id
}

func (r *BaseRecord) GetTimestamp() *time.Time {
	return r.Timestamp
}

func (r *BaseRecord) SetTimestamp(timestamp *time.Time) {
	r.Timestamp = timestamp
}

// LineDbOptions представляет опции для создания LineDb
// Соответствует TypeScript интерфейсу LineDbOptions
type LineDbOptions struct {
	CacheSize   int           `json:"cacheSize,omitempty"`
	CacheTTL    time.Duration `json:"cacheTTL,omitempty"`
	DBFolder    string        `json:"dbFolder,omitempty"`
	ObjName     string        `json:"objName,omitempty"`
	IndexStore  IndexStore    `json:"-"` // Хранилище индексов (nil = не использовать; по умолчанию InMemoryIndexStore при наличии IndexedFields)
}

// LineDbInitOptions представляет опции инициализации LineDb
// Соответствует TypeScript интерфейсу LineDbInitOptions
type LineDbInitOptions struct {
	CacheSize       int                   `json:"cacheSize,omitempty"`
	CacheTTL        time.Duration         `json:"cacheTTL,omitempty"`
	Collections     []JSONLFileOptions    `json:"collections"`
	DBFolder        string                `json:"dbFolder,omitempty"`
	Partitions       []PartitionCollection `json:"partitions,omitempty"`
	// IndexRebuildTimer — интервал в секундах; если > 0 — в отдельной горутине периодический полный ребилд всех индексов с блокировкой базы (db.mutex).
	IndexRebuildTimer int `json:"indexRebuildTimer,omitempty"`
}

// EmptyValueMode определяет, что считать "пустым" при проверке required/unique
type EmptyValueMode uint

const (
	EmptyModeAbsentKey   EmptyValueMode = 1 << iota // ключ отсутствует в записи
	EmptyModeNil                                    // значение nil
	EmptyModeZeroValue                              // "" для string, 0 для int/float; для bool не используется
)

// DefaultEmptyModeForRequired — режим по умолчанию для required (все виды пустоты)
const DefaultEmptyModeForRequired = EmptyModeAbsentKey | EmptyModeNil | EmptyModeZeroValue

// DefaultEmptyModeForUnique — режим по умолчанию для unique (nil и "" — пропускаем проверку)
const DefaultEmptyModeForUnique = EmptyModeNil | EmptyModeZeroValue

// FieldConstraint — конфигурация ограничения на поле (required/unique)
type FieldConstraint struct {
	Name      string         `json:"name"`
	Required  bool           `json:"required,omitempty"`
	Unique    bool           `json:"unique,omitempty"`
	EmptyMode EmptyValueMode `json:"emptyMode,omitempty"` // для required: что считать пустым; для unique: когда пропускать проверку
}

// KeyOrder задаёт порядок ключей при сериализации JSON.
// Order >= 0: с начала (0, 1, 2...). Order < 0: с конца (-1 = последний, -2 = предпоследний).
// Ключи не из списка — в середине, по алфавиту.
type KeyOrder struct {
	Key   string `json:"key"`
	Order int    `json:"order"`
}

// JSONLFileOptions представляет опции для JSONL файла
// Соответствует TypeScript интерфейсу JSONLFileOptions
type JSONLFileOptions struct {
	CollectionName          string             `json:"collectionName,omitempty"`
	AllocSize               int                `json:"allocSize,omitempty"`
	IndexedFields           []string           `json:"indexedFields,omitempty"`
	FieldConstraints        []FieldConstraint  `json:"fieldConstraints,omitempty"` // Ограничения полей (required/unique)
	UniqueFields            []string           `json:"uniqueFields,omitempty"`     // Устаревшее: поля с уникальностью (совместимость)
	KeyOrder                []KeyOrder         `json:"keyOrder,omitempty"`         // Порядок ключей при сериализации (если задан — используется кастомная сериализация)
	Encode                  bool               `json:"encode,omitempty"`           // Шифровать записи после сериализации
	EncodeKey               string             `json:"encodeKey,omitempty"`        // Ключ (строка -> SHA256 для байтового ключа), требуется при Encode
	EncryptFn               func(plaintext []byte, key []byte) ([]byte, error)     `json:"-"` // Кастомное шифрование (если nil — встроенное AES-GCM)
	DecryptFn               func(ciphertext []byte, key []byte) ([]byte, error)    `json:"-"` // Кастомная расшифровка (если nil — встроенное AES-GCM)
	EncryptKeyForLineDb     string   `json:"encryptKeyForLineDb,omitempty"`
	SkipInvalidLines        bool     `json:"skipInvalidLines,omitempty"`
	DecryptKey              string   `json:"decryptKey,omitempty"`
	ConvertStringIdToNumber bool     `json:"convertStringIdToNumber,omitempty"`
	// RebuildIndexesOnFileChange — после каждой мутации файла коллекции (Insert/Update/Delete через LineDb)
	// полная пересборка всех индексов по этой логической коллекции; частичное индексирование (IndexRecord),
	// tryIndexUpdate/tryIndexDelete и периодический ребилд по таймеру для этой коллекции отключаются.
	RebuildIndexesOnFileChange bool `json:"rebuildIndexesOnFileChange,omitempty"`
	// Функции сериализации и десериализации JSON
	JSONMarshal   func(any) ([]byte, error) `json:"-"`
	JSONUnmarshal func([]byte, any) error   `json:"-"`
}

// PartitionCollection представляет конфигурацию партиционирования
// Соответствует TypeScript интерфейсу PartitionCollection
type PartitionCollection struct {
	CollectionName string           `json:"collectionName"`
	PartIDFn       func(any) string `json:"-"`
	PartIDFnStr    string           `json:"partIdFn,omitempty"`
}

// JoinType представляет тип операции JOIN
type JoinType string

const (
	JoinTypeInner JoinType = "inner"
	JoinTypeLeft  JoinType = "left"
	JoinTypeRight JoinType = "right"
	JoinTypeFull  JoinType = "full"
)

// JoinOptions представляет опции для операции JOIN
// Соответствует TypeScript интерфейсу JoinOptions
type JoinOptions struct {
	Type             JoinType       `json:"type"`
	LeftFields       []string       `json:"leftFields"`
	RightFields      []string       `json:"rightFields"`
	StrictCompare    bool           `json:"strictCompare,omitempty"`
	InTransaction    bool           `json:"inTransaction,omitempty"`
	TransactionID    string         `json:"transactionId,omitempty"`
	LeftFilter       map[string]any `json:"leftFilter,omitempty"`
	RightFilter      map[string]any `json:"rightFilter,omitempty"`
	OnlyOneFromRight bool           `json:"onlyOneFromRight,omitempty"`
}

// PaginatedResult представляет результат пагинации
// Соответствует TypeScript интерфейсу PaginatedResult
type PaginatedResult struct {
	Data  []any `json:"data"`
	Total int   `json:"total"`
	Limit int   `json:"limit"`
	Pages int   `json:"pages"`
	Page  int   `json:"page"`
}

// BackupMetaData представляет метаданные резервной копии
// Соответствует TypeScript интерфейсу BackupMetaData
type BackupMetaData struct {
	CollectionNames []string `json:"collectionNames"`
	Gzip            bool     `json:"gzip"`
	EncryptKey      string   `json:"encryptKey"`
	NoLock          bool     `json:"noLock"`
	Timestamp       int64    `json:"timestamp"`
	BackupDate      string   `json:"backupDate"`
}

// FilterFunction представляет функцию фильтрации
type FilterFunction func(data any) bool

// LineDbAdapterOptions представляет опции для операций с адаптером
// Соответствует TypeScript интерфейсу LineDbAdapterOptions
type LineDbAdapterOptions struct {
	InTransaction             bool   `json:"inTransaction,omitempty"`
	StrictCompare             bool   `json:"strictCompare,omitempty"`
	TransactionID             string `json:"transactionId,omitempty"`
	DebugTag                  string `json:"debugTag,omitempty"`
	FilterType                string `json:"filterType,omitempty"`
	Method                    string `json:"method,omitempty"`
	RepeatCount               int    `json:"repeatCount,omitempty"`
	InternalCall              bool   `json:"internalCall,omitempty"`
	SkipCheckExistingForWrite bool   `json:"skipCheckExistingForWrite,omitempty"`
	OptimisticRead            bool   `json:"optimisticRead,omitempty"`
	ReturnChain               bool   `json:"returnChain,omitempty"`
	DoIndexing                bool   `json:"doIndexing,omitempty"` // при true — после Write делать точечное индексирование новых записей
	// FailOnFailureIndexRead — при true: если ReadByFilter пошёл по индексу и получил ошибку (Lookup / ReadByLineIndexes),
	// вернуть её и не делать полный перебор по коллекции.
	FailOnFailureIndexRead bool `json:"failOnFailureIndexRead,omitempty"`
}

// TransactionOptions представляет опции транзакции
// Соответствует TypeScript интерфейсу TransactionOptions
type TransactionOptions struct {
	Rollback              bool   `json:"rollback,omitempty"`
	BackupFile            string `json:"backupFile,omitempty"`
	DoNotDeleteBackupFile bool   `json:"doNotDeleteBackupFile,omitempty"`
	Timeout               int    `json:"timeout,omitempty"`
}

// LineDbTransactionOptions представляет опции транзакции LineDb
// Соответствует TypeScript интерфейсу LineDbTransactionOptions
type LineDbTransactionOptions struct {
	Rollback              bool   `json:"rollback,omitempty"`
	BackupFile            string `json:"backupFile,omitempty"`
	DoNotDeleteBackupFile bool   `json:"doNotDeleteBackupFile,omitempty"`
	Timeout               int    `json:"timeout,omitempty"`
}

// JoinResult представляет результат операции JOIN
type JoinResult struct {
	Left  any `json:"left"`
	Right any `json:"right"`
}

// CollectionChain представляет цепочку коллекций (аналог lodash chain)
// Улучшенная версия с дополнительными методами
type CollectionChain struct {
	data []any
}

// NewCollectionChain создает новую цепочку коллекций
func NewCollectionChain(data []any) *CollectionChain {
	return &CollectionChain{data: data}
}

// Value возвращает данные цепочки
func (c *CollectionChain) Value() []any {
	return c.data
}

// Where фильтрует данные
func (c *CollectionChain) Where(filter func(any) bool) *CollectionChain {
	var filtered []any
	for _, item := range c.data {
		if filter(item) {
			filtered = append(filtered, item)
		}
	}
	return &CollectionChain{data: filtered}
}

// Sort сортирует данные
func (c *CollectionChain) Sort(compare func(a, b any) bool) *CollectionChain {
	// Используем более эффективную сортировку
	sorted := make([]any, len(c.data))
	copy(sorted, c.data)

	// Quick sort implementation
	c.quickSort(sorted, 0, len(sorted)-1, compare)

	return &CollectionChain{data: sorted}
}

// quickSort реализует быструю сортировку
func (c *CollectionChain) quickSort(arr []any, low, high int, compare func(a, b any) bool) {
	if low < high {
		pi := c.partition(arr, low, high, compare)
		c.quickSort(arr, low, pi-1, compare)
		c.quickSort(arr, pi+1, high, compare)
	}
}

// partition вспомогательная функция для quickSort
func (c *CollectionChain) partition(arr []any, low, high int, compare func(a, b any) bool) int {
	pivot := arr[high]
	i := low - 1

	for j := low; j < high; j++ {
		if compare(arr[j], pivot) {
			i++
			arr[i], arr[j] = arr[j], arr[i]
		}
	}
	arr[i+1], arr[high] = arr[high], arr[i+1]
	return i + 1
}

// Map применяет функцию к каждому элементу
func (c *CollectionChain) Map(fn func(any) any) *CollectionChain {
	mapped := make([]any, len(c.data))
	for i, item := range c.data {
		mapped[i] = fn(item)
	}
	return &CollectionChain{data: mapped}
}

// Take возвращает первые n элементов
func (c *CollectionChain) Take(n int) *CollectionChain {
	if n >= len(c.data) {
		return c
	}
	return &CollectionChain{data: c.data[:n]}
}

// Skip пропускает первые n элементов
func (c *CollectionChain) Skip(n int) *CollectionChain {
	if n >= len(c.data) {
		return &CollectionChain{data: []any{}}
	}
	return &CollectionChain{data: c.data[n:]}
}

// Size возвращает размер коллекции
func (c *CollectionChain) Size() int {
	return len(c.data)
}

// IsEmpty проверяет, пуста ли коллекция
func (c *CollectionChain) IsEmpty() bool {
	return len(c.data) == 0
}

// First возвращает первый элемент
func (c *CollectionChain) First() any {
	if len(c.data) == 0 {
		return nil
	}
	return c.data[0]
}

// Last возвращает последний элемент
func (c *CollectionChain) Last() any {
	if len(c.data) == 0 {
		return nil
	}
	return c.data[len(c.data)-1]
}
