package postgresql

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"log/slog"

	"gitea.computernetthings.ru/yash/crypto_alert_bot/internal/config"
	"gitea.computernetthings.ru/yash/crypto_alert_bot/internal/repository/postgresql/migrations"

	"github.com/jackc/pgx/v5/pgxpool"

	"github.com/golang-migrate/migrate/v4"
	pgx "github.com/golang-migrate/migrate/v4/database/pgx/v5"
	"github.com/golang-migrate/migrate/v4/source/iofs"
	_ "github.com/jackc/pgx/v5/stdlib"
)

type Postgresql struct {
	db *pgxpool.Pool
}

func New(ctx context.Context, log *slog.Logger, cfg *config.Postgresql) (*Postgresql, error) {
	pool, err := pgxpool.New(ctx, fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=disable", cfg.User, cfg.Password, cfg.Address, cfg.DBName))
	if err != nil {
		return nil, fmt.Errorf("failed to connect to postgres: %w", err)
	}

	if err = pool.Ping(ctx); err != nil {
		return nil, err
	}

	// apply migrations automatically
	if err = applyMigrations(cfg, log); err != nil {
		return nil, fmt.Errorf("failed to apply migrations: %w", err)
	}

	return &Postgresql{db: pool}, nil
}

func applyMigrations(cfg *config.Postgresql, log *slog.Logger) error {
	dsn := fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=disable", cfg.User, cfg.Password, cfg.Address, cfg.DBName)

	sqlDB, err := sql.Open("pgx", dsn)
	if err != nil {
		return fmt.Errorf("failed to open sql db for migrations: %w", err)
	}
	defer sqlDB.Close()

	driver, err := pgx.WithInstance(sqlDB, &pgx.Config{})
	if err != nil {
		return fmt.Errorf("failed to create pgx driver: %w", err)
	}

	d, err := iofs.New(migrations.Folder, ".")
	if err != nil {
		return fmt.Errorf("failed to create iofs source: %w", err)
	}

	m, err := migrate.NewWithInstance("iofs", d, "postgres", driver)
	if err != nil {
		return fmt.Errorf("failed to create postgresql db migration: %w", err)
	}

	if err := m.Up(); err != nil {
		if errors.Is(err, migrate.ErrNoChange) {
			log.Info("no migrations to process")
			return nil
		}
		return err
	}
	log.Info("migrations processed")
	return nil
}
