package infra

import (
	"database/sql"
	"fmt"
	"time"
)

// MigrationDown reverts the last n applied migrations by executing their down_sql
// from the _migrations table. Migrations are reverted in reverse version order
// (highest version first). Each reversion runs in its own transaction.
// Returns the list of reverted migrations. If n <= 0, no migrations are reverted.
func MigrationDown(db *sql.DB, n int) ([]Migration, error) {
	if n <= 0 {
		return nil, nil
	}

	// Fetch last n applied migrations in descending order
	const query = `
		SELECT version, name, up_sql, down_sql, applied_at
		FROM _migrations
		ORDER BY version DESC
		LIMIT ?`

	rows, err := db.Query(query, n)
	if err != nil {
		return nil, fmt.Errorf("migration_down: query _migrations: %w", err)
	}
	defer rows.Close()

	var toRevert []Migration
	for rows.Next() {
		var m Migration
		var appliedAtStr string
		if err := rows.Scan(&m.Version, &m.Name, &m.UpSQL, &m.DownSQL, &appliedAtStr); err != nil {
			return nil, fmt.Errorf("migration_down: scan row: %w", err)
		}
		m.AppliedAt, _ = time.Parse("2006-01-02 15:04:05", appliedAtStr)
		toRevert = append(toRevert, m)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("migration_down: rows error: %w", err)
	}

	// Revert each migration in its own transaction (already in DESC order)
	var reverted []Migration
	for _, m := range toRevert {
		if err := revertMigration(db, m); err != nil {
			return reverted, fmt.Errorf("migration_down: reverting version %d (%s): %w", m.Version, m.Name, err)
		}
		reverted = append(reverted, m)
	}

	return reverted, nil
}

// revertMigration executes a migration's DownSQL within a transaction and removes it
// from _migrations. If DownSQL is empty, only the record is removed.
func revertMigration(db *sql.DB, m Migration) error {
	tx, err := db.Begin()
	if err != nil {
		return fmt.Errorf("begin transaction: %w", err)
	}
	defer tx.Rollback() //nolint:errcheck

	// Execute the down SQL if present
	if m.DownSQL != "" {
		if _, err := tx.Exec(m.DownSQL); err != nil {
			return fmt.Errorf("exec down_sql: %w", err)
		}
	}

	// Remove the migration record
	if _, err := tx.Exec("DELETE FROM _migrations WHERE version = ?", m.Version); err != nil {
		return fmt.Errorf("delete from _migrations: %w", err)
	}

	return tx.Commit()
}
