// internal/infrastructure/database/database.go
package database

import (
    "context"
    "crypto/tls"
    "database/sql"
    "fmt"
    "log"
    "os"
    "path/filepath"
    "runtime"
    "sort"
    "strconv"
    "strings"
    "sync"
    "time"

    "service-general/internal/infrastructure/config"
    "service-general/pkg/logger" // Import logger Anda

    "github.com/jackc/pgx/v5"
    "github.com/jackc/pgx/v5/stdlib"
    "github.com/jmoiron/sqlx"
    "github.com/lib/pq" // PASTIKAN IMPORT INI ADA

    // Driver GORM di-import secara eksplisit untuk Migration
    "gorm.io/driver/mysql"
    "gorm.io/driver/postgres"
    "gorm.io/driver/sqlite"
    "gorm.io/driver/sqlserver"
    "gorm.io/gorm"
    "gorm.io/gorm/logger" // Import logger dari GORM

    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

// ... (tipe DatabaseType dan interface Service tetap sama)

type service struct {
    // GORM DB adalah sumber utama
    gormDatabases map[string]*gorm.DB
    // Map untuk menyimpan koneksi mentah yang diekstrak dari GORM
    sqlDatabases  map[string]*sql.DB
    sqlxDatabases map[string]*sqlx.DB
    mongoClients  map[string]*mongo.Client
    readReplicas  map[string][]*sql.DB
    configs       map[string]config.DatabaseConfig
    readConfigs   map[string][]config.DatabaseConfig
    mu            sync.RWMutex
    readBalancer  map[string]int
    // PERBAIKAN 1: Kembali ke tipe yang benar untuk listener PostgreSQL
    listeners   map[string]*pq.Listener
    listenersMu sync.RWMutex
}

var (
    dbManager *service
    once      sync.Once
)

// ... (fungsi New dan loadFromConfig tetap sama)

// --- PERBAIKAN 2: Buat Adapter untuk Logger GORM ---

// gormLoggerAdapter adalah adapter untuk menghubungkan logger kita dengan GORM
type gormLoggerAdapter struct {
    logger logger.Logger
}

// NewGormLoggerAdapter membuat instance baru dari adapter
func NewGormLoggerAdapter(l logger.Logger) logger.Interface {
    // Tentukan level log GORM berdasarkan level logger kita
    // Ini adalah implementasi sederhana, Anda bisa membuatnya lebih dinamis
    logLevel := logger.Silent
    if l != nil {
        // Anda bisa menambahkan logika untuk menentukan level dari logger Anda
        // Untuk sekarang, kita hardcode ke Info
        logLevel = logger.Info
    }

    return &gormLoggerAdapter{
        logger: l,
        // Konfigurasi logger GORM
        Config: logger.Config{
            SlowThreshold:             200 * time.Millisecond,
            LogLevel:                  logLevel,
            IgnoreRecordNotFoundError: false,
            Colorful:                  false,
        },
    }
}

// LogMode menetapkan level log
func (l *gormLoggerAdapter) LogMode(level logger.LogLevel) logger.Interface {
    newLogger := *l
    newLogger.Config.LogLevel = level
    return &newLogger
}

// Info mencatat log info
func (l *gormLoggerAdapter) Info(ctx context.Context, msg string, data ...interface{}) {
    l.logger.WithContext(ctx).Info(fmt.Sprintf(msg, data...))
}

// Warn mencatat log warning
func (l *gormLoggerAdapter) Warn(ctx context.Context, msg string, data ...interface{}) {
    l.logger.WithContext(ctx).Warn(fmt.Sprintf(msg, data...))
}

// Error mencatat log error
func (l *gormLoggerAdapter) Error(ctx context.Context, msg string, data ...interface{}) {
    l.logger.WithContext(ctx).Error(fmt.Sprintf(msg, data...))
}

// Trace mencatat log trace (SQL query)
func (l *gormLoggerAdapter) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
    elapsed := time.Since(begin)
    sql, rows := fc()

    fields := map[string]interface{}{
        "duration":  elapsed,
        "rows":      rows,
        "sql":       sql,
        "error":     err,
        "function":  "database.trace",
    }

    switch {
    case err != nil && l.Config.LogLevel >= logger.Error:
        l.logger.WithContext(ctx).WithFields(logger.Any("error", err)).Error("Database error", logger.Any("details", fields))
    case elapsed > l.Config.SlowThreshold && l.Config.LogLevel >= logger.Warn:
        l.logger.WithContext(ctx).Warn("Slow database query", logger.Any("details", fields))
    case l.Config.LogLevel == logger.Info:
        l.logger.WithContext(ctx).Debug("Database query", logger.Any("details", fields))
    }
}

// --- Metode Koneksi GORM (diperbaiki untuk menggunakan adapter) ---

func (s *service) openPostgresGORM(config config.DatabaseConfig) (*gorm.DB, error) {
    dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=Asia/Jakarta",
        config.Host, config.Username, config.Password, config.Database, config.Port, config.SSLMode)

    if config.Schema != "" {
        dsn += fmt.Sprintf(" search_path=%s", config.Schema)
    }

    // PERBAIKAN: Gunakan adapter logger kita
    gormConfig := &gorm.Config{
        Logger: NewGormLoggerAdapter(logger.Default()),
    }

    if config.RequireSSL {
        // ... (konfigurasi SSL tetap sama)
    }

    return gorm.Open(postgres.Open(dsn), gormConfig)
}

func (s *service) openMySQLGORM(config config.DatabaseConfig) (*gorm.DB, error) {
    dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
        config.Username, config.Password, config.Host, config.Port, config.Database)

    if config.RequireSSL {
        dsn += "&tls=true"
    }

    // PERBAIKAN: Gunakan adapter logger kita
    gormConfig := &gorm.Config{
        Logger: NewGormLoggerAdapter(logger.Default()),
    }

    return gorm.Open(mysql.Open(dsn), gormConfig)
}

func (s *service) openSQLServerGORM(config config.DatabaseConfig) (*gorm.DB, error) {
    dsn := fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s",
        config.Username, config.Password, config.Host, config.Port, config.Database)

    if config.RequireSSL {
        // ... (konfigurasi SSL tetap sama)
    }

    // PERBAIKAN: Gunakan adapter logger kita
    gormConfig := &gorm.Config{
        Logger: NewGormLoggerAdapter(logger.Default()),
    }

    return gorm.Open(sqlserver.Open(dsn), gormConfig)
}

func (s *service) openSQLiteGORM(config config.DatabaseConfig) (*gorm.DB, error) {
    // PERBAIKAN: Gunakan adapter logger kita
    gormConfig := &gorm.Config{
        Logger: NewGormLoggerAdapter(logger.Default()),
    }

    return gorm.Open(sqlite.Open(config.Path), gormConfig)
}

// --- Metode Lainnya ---

// ... (semua metode lain seperti GetGormDB, GetDB, Migrate, Health, Close, dll. tidak perlu diubah)
// ... (kecuali mungkin bagian Close yang membersihkan listeners)

// Di fungsi Close, pastikan tipe listener sudah benar
func (s *service) Close() error {
    s.mu.Lock()
    defer s.mu.Unlock()

    var errs []error

    for name, listener := range s.listeners {
        if err := listener.Close(); err != nil {
            errs = append(errs, fmt.Errorf("failed to close listener for %s: %w", name, err))
        }
    }
    // ... (sisa kode Close)
}

// Di fungsi ListenForChanges, pastikan Anda menggunakan pq.NewListener
func (s *service) ListenForChanges(ctx context.Context, dbName string, channels []string, callback func(string, string)) error {
    // ... (kode Anda sudah benar menggunakan pq.NewListener)
    // ...
}