package database

import (
	"fmt"
	"strings"

	"go.uber.org/zap"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"

	"api_bb/pkg/logger"
)

type Database struct {
	DB  *gorm.DB
	cfg *Config
}

type Config struct {
	URL string
}

func NewDatabase(cfg *Config) *Database {
	return &Database{
		cfg: cfg,
	}
}

// Connect устанавливает соединение с базой данных
func (d *Database) Connect() error {
	zapLogger := logger.Get()

	// Логирование попытки подключения к БД
	zapLogger.Info("attempting to connect to database",
		zap.String("host", ExtractHostFromDSN(d.cfg.URL)),
		zap.String("database", ExtractDBNameFromDSN(d.cfg.URL)),
	)

	db, err := gorm.Open(postgres.Open(d.cfg.URL), &gorm.Config{})
	if err != nil {
		zapLogger.Error("failed to connect to database",
			zap.Error(err),
			zap.String("database_url", MaskPassword(d.cfg.URL)),
		)
		return fmt.Errorf("failed to connect to database: %w", err)
	}

	d.DB = db

	// Логирование успешного подключения к БД
	zapLogger.Info("successfully connected to database",
		zap.String("host", ExtractHostFromDSN(d.cfg.URL)),
		zap.String("database", ExtractDBNameFromDSN(d.cfg.URL)),
	)

	return nil
}

// Ping проверяет соединение с базой данных
func (d *Database) Ping() error {
	zapLogger := logger.Get()

	sqlDB, err := d.DB.DB()
	if err != nil {
		zapLogger.Error("failed to get database instance", zap.Error(err))
		return fmt.Errorf("failed to get database instance: %w", err)
	}

	if err := sqlDB.Ping(); err != nil {
		zapLogger.Error("database ping failed", zap.Error(err))
		return fmt.Errorf("database ping failed: %w", err)
	}

	zapLogger.Info("database ping successful")
	return nil
}

// Close закрывает соединение с базой данных
func (d *Database) Close() error {
	zapLogger := logger.Get()

	if d.DB == nil {
		return nil
	}

	sqlDB, err := d.DB.DB()
	if err != nil {
		zapLogger.Error("failed to get database instance for closing", zap.Error(err))
		return fmt.Errorf("failed to get database instance: %w", err)
	}

	zapLogger.Info("closing database connection")
	if err := sqlDB.Close(); err != nil {
		zapLogger.Error("failed to close database connection", zap.Error(err))
		return fmt.Errorf("failed to close database connection: %w", err)
	}

	zapLogger.Info("database connection closed successfully")
	return nil
}

// Вспомогательные функции для работы с DSN

// ExtractHostFromDSN извлекает хост из DSN строки
func ExtractHostFromDSN(dsn string) string {
	// Простая реализация для PostgreSQL DSN
	parts := strings.Split(dsn, " ")
	for _, part := range parts {
		if strings.HasPrefix(part, "host=") {
			return strings.TrimPrefix(part, "host=")
		}
	}
	return "unknown"
}

// ExtractDBNameFromDSN извлекает имя базы данных из DSN строки
func ExtractDBNameFromDSN(dsn string) string {
	// Простая реализация для PostgreSQL DSN
	parts := strings.Split(dsn, " ")
	for _, part := range parts {
		if strings.HasPrefix(part, "dbname=") {
			return strings.TrimPrefix(part, "dbname=")
		}
	}
	return "unknown"
}

// MaskPassword маскирует пароль в DSN строке для безопасного логирования
func MaskPassword(dsn string) string {
	// Простая реализация - заменяет пароль на ***
	parts := strings.Split(dsn, " ")
	for i, part := range parts {
		if strings.HasPrefix(part, "password=") {
			parts[i] = "password=***"
			break
		}
	}
	return strings.Join(parts, " ")
}