package banco

import (
	"database/sql"
	"fmt"
)

type GerenciadorConexao struct {
	DSN       string
	MaxConns  int
	db        *sql.DB
	abrirFunc func(string, int) (*sql.DB, error)
}

func NovoGerenciador(dsn string, maxConns int) *GerenciadorConexao {
	return &GerenciadorConexao{
		DSN:       dsn,
		MaxConns:  maxConns,
		abrirFunc: Abrir,
	}
}

func (g *GerenciadorConexao) DB() (*sql.DB, error) {
	if g.db != nil {
		if err := g.db.Ping(); err == nil {
			return g.db, nil
		}
		Fechar(g.db)
		g.db = nil
	}
	db, err := g.abrirFunc(g.DSN, g.MaxConns)
	if err != nil {
		return nil, err
	}
	g.db = db
	return db, nil
}

func (g *GerenciadorConexao) GarantirSaude() error {
	_, err := g.DB()
	return err
}

func (g *GerenciadorConexao) Reconectar() error {
	Fechar(g.db)
	g.db = nil
	_, err := g.DB()
	if err != nil {
		return fmt.Errorf("reconectar MySQL: %w", err)
	}
	return nil
}

func (g *GerenciadorConexao) Fechar() {
	Fechar(g.db)
	g.db = nil
}

func (g *GerenciadorConexao) ExecutarComSaude(fn func(*sql.DB) error) error {
	return Retentar(5, func() error {
		db, err := g.DB()
		if err != nil {
			return err
		}
		if err := db.Ping(); err != nil {
			if recErr := g.Reconectar(); recErr != nil {
				return recErr
			}
			db, err = g.DB()
			if err != nil {
				return err
			}
		}
		err = fn(db)
		if err != nil && EhRetentavel(err) {
			_ = g.Reconectar()
		}
		return err
	})
}
