package data

import (
	"context"
	"log/slog"
	"sync"
	"time"

	"gorm.io/gorm"
)

const (
	dataScopeAuditQueueSize = 1024
	dataScopeAuditBatchSize = 100
	dataScopeAuditInterval  = 2 * time.Second
)

// dataScopeAuditWriter keeps row-scope auditing best-effort: callbacks only
// enqueue, while a background worker writes batches to the active database.
type dataScopeAuditWriter struct {
	data      *Data
	logger    *slog.Logger
	queue     chan dataAccessLogPO
	batchSize int
	interval  time.Duration
	stop      chan struct{}
	done      chan struct{}
	closeOnce sync.Once
}

func newDataScopeAuditWriter(data *Data, logger *slog.Logger) *dataScopeAuditWriter {
	return newDataScopeAuditWriterWithOptions(data, logger, dataScopeAuditQueueSize, dataScopeAuditBatchSize, dataScopeAuditInterval)
}

func newDataScopeAuditWriterWithOptions(data *Data, logger *slog.Logger, queueSize, batchSize int, interval time.Duration) *dataScopeAuditWriter {
	w := &dataScopeAuditWriter{
		data:      data,
		logger:    logger,
		queue:     make(chan dataAccessLogPO, queueSize),
		batchSize: batchSize,
		interval:  interval,
		stop:      make(chan struct{}),
		done:      make(chan struct{}),
	}
	go w.run()
	return w
}

func (d *Data) enqueueDataScopeAudit(record dataAccessLogPO) {
	if d == nil || d.auditLog == nil {
		return
	}
	d.auditLog.Enqueue(record)
}

func (w *dataScopeAuditWriter) Enqueue(record dataAccessLogPO) {
	if w == nil {
		return
	}
	select {
	case w.queue <- record:
	default:
		w.log().Warn("数据权限审计缓冲已满, 事件被丢弃", "mod", "data-scope", "event_type", record.EventType, "table", record.TargetTable)
	}
}

func (w *dataScopeAuditWriter) Close() {
	if w == nil {
		return
	}
	w.closeOnce.Do(func() { close(w.stop) })
	<-w.done
}

func (w *dataScopeAuditWriter) run() {
	defer close(w.done)
	ticker := time.NewTicker(w.interval)
	defer ticker.Stop()
	batch := make([]dataAccessLogPO, 0, w.batchSize)
	flush := func() {
		if len(batch) == 0 || w.data == nil || w.data.gormDB == nil {
			return
		}
		db := w.data.gormDB.DB()
		if db == nil {
			return
		}
		if err := db.WithContext(context.Background()).Session(&gorm.Session{NewDB: true, SkipHooks: true}).Create(&batch).Error; err != nil {
			w.log().Warn("数据权限审计批量写入失败", "mod", "data-scope", "error", err)
		}
		batch = batch[:0]
	}
	for {
		select {
		case record := <-w.queue:
			batch = append(batch, record)
			if len(batch) >= w.batchSize {
				flush()
			}
		case <-ticker.C:
			flush()
		case <-w.stop:
			for {
				select {
				case record := <-w.queue:
					batch = append(batch, record)
				default:
					flush()
					return
				}
			}
		}
	}
}

func (w *dataScopeAuditWriter) log() *slog.Logger {
	if w != nil && w.logger != nil {
		return w.logger
	}
	return slog.Default()
}
