package database

import (
	"context"
	"database/sql"
	"fmt"

	"dbbackup/internal/config"
	"dbbackup/internal/logger"

	_ "github.com/go-sql-driver/mysql" // MySQL driver
	_ "github.com/jackc/pgx/v5/stdlib" // PostgreSQL driver (pgx - high performance)
)

// Database represents a database connection and operations
type Database interface {
	// Connection management
	Connect(ctx context.Context) error
	Close() error
	Ping(ctx context.Context) error

	// Database discovery
	ListDatabases(ctx context.Context) ([]string, error)
	ListTables(ctx context.Context, database string) ([]string, error)

	// Database operations
	CreateDatabase(ctx context.Context, name string) error
	DropDatabase(ctx context.Context, name string) error
	DatabaseExists(ctx context.Context, name string) (bool, error)

	// Information
	GetVersion(ctx context.Context) (string, error)
	GetDatabaseSize(ctx context.Context, database string) (int64, error)
	GetTableRowCount(ctx context.Context, database, table string) (int64, error)

	// Backup/Restore command building
	BuildBackupCommand(database, outputFile string, options BackupOptions) []string
	BuildRestoreCommand(database, inputFile string, options RestoreOptions) []string
	BuildSampleQuery(database, table string, strategy SampleStrategy) string

	// GetPasswordEnvVar returns the environment variable for passing the password
	// to external commands (e.g., MYSQL_PWD, PGPASSWORD). Returns empty if password
	// should be passed differently (e.g., via .pgpass file) or is not set.
	GetPasswordEnvVar() string

	// Validation
	ValidateBackupTools() error
}

// BackupOptions holds options for backup operations
type BackupOptions struct {
	Compression  int
	Parallel     int
	Format       string // "custom", "plain", "directory"
	Blobs        bool
	SchemaOnly   bool
	DataOnly     bool
	NoOwner      bool
	NoPrivileges bool
	Clean        bool
	IfExists     bool
	Role         string
}

// RestoreOptions holds options for restore operations
type RestoreOptions struct {
	Parallel          int
	Clean             bool
	IfExists          bool
	NoOwner           bool
	NoPrivileges      bool
	SingleTransaction bool
	Verbose           bool // Enable verbose output (caution: can cause OOM on large restores)
}

// SampleStrategy defines how to sample data
type SampleStrategy struct {
	Type  string // "ratio", "percent", "count"
	Value int
}

// DatabaseInfo holds database metadata
type DatabaseInfo struct {
	Name      string
	Size      int64
	Owner     string
	Encoding  string
	Collation string
	Tables    []TableInfo
}

// TableInfo holds table metadata
type TableInfo struct {
	Schema   string
	Name     string
	RowCount int64
	Size     int64
}

// New creates a new database instance based on configuration
func New(cfg *config.Config, log logger.Logger) (Database, error) {
	if cfg.IsPostgreSQL() {
		return NewPostgreSQL(cfg, log), nil
	} else if cfg.IsMySQL() {
		return NewMySQL(cfg, log), nil
	}
	return nil, fmt.Errorf("unsupported database type: %s", cfg.DatabaseType)
}

// Common database implementation
type baseDatabase struct {
	cfg *config.Config
	log logger.Logger
	db  *sql.DB
	dsn string
}

func (b *baseDatabase) Close() error {
	if b.db != nil {
		return b.db.Close()
	}
	return nil
}

func (b *baseDatabase) GetConn() *sql.DB {
	return b.db
}

func (b *baseDatabase) Ping(ctx context.Context) error {
	if b.db == nil {
		return fmt.Errorf("database not connected")
	}
	return b.db.PingContext(ctx)
}
