// migrate - Script para migrar dados do BoltDB para SQLite3
package main

import (
	"crypto/sha256"
	"encoding/hex"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"os"
	"time"

	"database/sql"

	"github.com/boltdb/bolt"
	_ "modernc.org/sqlite"
)

const shortIDLength = 7

// generateShortID gera um ID único de 7 caracteres
func generateShortID() string {
	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	b := make([]byte, shortIDLength)
	for i := range b {
		b[i] = charset[rand.Intn(len(charset))]
	}
	return string(b)
}

// generateUniqueShortID gera um ID único que não existe no banco
func generateUniqueShortID(db *sql.DB) (string, error) {
	maxAttempts := 100
	for i := 0; i < maxAttempts; i++ {
		id := generateShortID()

		// Verificar se o ID já existe
		var exists int
		err := db.QueryRow("SELECT 1 FROM pastes WHERE short_id = ? LIMIT 1", id).Scan(&exists)
		if err == sql.ErrNoRows {
			return id, nil
		}
		if err != nil {
			return "", err
		}
	}
	return "", fmt.Errorf("não foi possível gerar um ID único após %d tentativas", maxAttempts)
}

func main() {
	// Initialize random seed
	rand.Seed(time.Now().UnixNano())

	var boltPath, sqlitePath string
	flag.StringVar(&boltPath, "bolt", "", "caminho para o arquivo BoltDB")
	flag.StringVar(&sqlitePath, "sqlite", "", "caminho para o arquivo SQLite3 de destino")
	flag.Parse()

	if boltPath == "" || sqlitePath == "" {
		fmt.Fprintf(os.Stderr, "Uso: %s -bolt <arquivo_bolt.db> -sqlite <arquivo_sqlite.db>\n", os.Args[0])
		os.Exit(1)
	}

	// Abrir banco BoltDB
	boltDB, err := bolt.Open(boltPath, 0600, &bolt.Options{Timeout: 1 * time.Second})
	if err != nil {
		log.Fatal("Erro ao abrir BoltDB:", err)
	}
	defer boltDB.Close()

	// Abrir banco SQLite3
	sqliteDB, err := sql.Open("sqlite", sqlitePath)
	if err != nil {
		log.Fatal("Erro ao abrir SQLite3:", err)
	}
	defer sqliteDB.Close()

	// Criar tabela no SQLite3
	_, err = sqliteDB.Exec(`
		CREATE TABLE IF NOT EXISTS pastes (
			short_id TEXT PRIMARY KEY,
			content TEXT NOT NULL,
			content_hash TEXT NOT NULL,
			created_at INTEGER NOT NULL
		)
	`)
	if err != nil {
		log.Fatal("Erro ao criar tabela:", err)
	}

	// Criar índices
	_, err = sqliteDB.Exec(`CREATE INDEX IF NOT EXISTS idx_pastes_content_hash ON pastes(content_hash)`)
	if err != nil {
		log.Fatal("Erro ao criar índice content_hash:", err)
	}

	_, err = sqliteDB.Exec(`CREATE INDEX IF NOT EXISTS idx_pastes_created_at ON pastes(created_at)`)
	if err != nil {
		log.Fatal("Erro ao criar índice created_at:", err)
	}

	// Iniciar transação no SQLite3
	tx, err := sqliteDB.Begin()
	if err != nil {
		log.Fatal("Erro ao iniciar transação:", err)
	}

	// Preparar statement de inserção
	stmt, err := tx.Prepare("INSERT OR IGNORE INTO pastes (short_id, content, content_hash, created_at) VALUES (?, ?, ?, ?)")
	if err != nil {
		log.Fatal("Erro ao preparar statement:", err)
	}
	defer stmt.Close()

	// Migrar dados do BoltDB para SQLite3
	count := 0
	duplicates := 0
	err = boltDB.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("pastes"))
		if b == nil {
			return fmt.Errorf("bucket 'pastes' não encontrado")
		}

		return b.ForEach(func(k, v []byte) error {
			// Calcular hash SHA-256 do conteúdo
			hash := sha256.Sum256(v)
			hashStr := hex.EncodeToString(hash[:])

			// Verificar se já existe um paste com o mesmo conteúdo
			var existingID string
			err := sqliteDB.QueryRow("SELECT short_id FROM pastes WHERE content_hash = ? LIMIT 1", hashStr).Scan(&existingID)
			if err == nil {
				// Paste já existe, pular
				duplicates++
				return nil
			} else if err != sql.ErrNoRows {
				return fmt.Errorf("erro ao verificar duplicata: %v", err)
			}

			// Gerar ID curto único
			shortID, err := generateUniqueShortID(sqliteDB)
			if err != nil {
				return fmt.Errorf("erro ao gerar ID único: %v", err)
			}

			// Inserir no SQLite3
			_, err = stmt.Exec(shortID, string(v), hashStr, time.Now().Unix())
			if err != nil {
				return fmt.Errorf("erro ao inserir paste %s: %v", shortID, err)
			}

			count++
			if count%100 == 0 {
				fmt.Printf("Migrados %d pastes...\n", count)
			}

			return nil
		})
	})

	if err != nil {
		tx.Rollback()
		log.Fatal("Erro durante migração:", err)
	}

	// Commit da transação
	err = tx.Commit()
	if err != nil {
		log.Fatal("Erro ao fazer commit:", err)
	}

	fmt.Printf("Migração concluída! %d pastes migrados com sucesso, %d duplicatas ignoradas.\n", count, duplicates)
}
