// migrate-to-sql - Script para migrar dados do BoltDB para arquivo SQL
package main

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

	"github.com/boltdb/bolt"
)

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 verificando contra um mapa
func generateUniqueShortID(usedIDs map[string]bool) string {
	maxAttempts := 100
	for i := 0; i < maxAttempts; i++ {
		id := generateShortID()
		if !usedIDs[id] {
			return id
		}
	}
	return ""
}

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

	var boltPath, sqlOutputPath string
	flag.StringVar(&boltPath, "bolt", "", "caminho para o arquivo BoltDB")
	flag.StringVar(&sqlOutputPath, "output", "", "caminho para o arquivo SQL de saída")
	flag.Parse()

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

	// Verificar se o arquivo BoltDB existe
	if _, err := os.Stat(boltPath); os.IsNotExist(err) {
		log.Fatal("Arquivo BoltDB não encontrado:", boltPath)
	}

	// 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()

	// Criar arquivo SQL de saída
	sqlFile, err := os.Create(sqlOutputPath)
	if err != nil {
		log.Fatal("Erro ao criar arquivo SQL:", err)
	}
	defer sqlFile.Close()

	// Escrever cabeçalho do arquivo SQL
	sqlFile.WriteString(`-- Script de migração do BoltDB para SQLite3
-- Gerado automaticamente em ` + time.Now().Format("02/01/2006 15:04:05") + `

-- Criar tabela se não existir
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
);

-- Criar índices
CREATE INDEX IF NOT EXISTS idx_pastes_content_hash ON pastes(content_hash);
CREATE INDEX IF NOT EXISTS idx_pastes_created_at ON pastes(created_at);

-- Inserir dados migrados
`)

	// Mapear hashes para IDs curtos para evitar duplicatas
	hashToShortID := make(map[string]string)
	usedShortIDs := make(map[string]bool)

	// Contadores
	totalPastes := 0
	duplicates := 0
	insertCount := 0

	// Migrar dados do BoltDB
	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 {
			totalPastes++

			// 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
			if existingShortID, exists := hashToShortID[hashStr]; exists {
				// Paste já existe, usar o mesmo ID curto
				duplicates++
				fmt.Printf("Duplicata encontrada: %s -> %s\n", hashStr[:8], existingShortID)
				return nil
			}

			// Gerar ID curto único
			shortID := generateUniqueShortID(usedShortIDs)
			if shortID == "" {
				return fmt.Errorf("não foi possível gerar ID único para paste %d", totalPastes)
			}

			// Marcar ID como usado
			usedShortIDs[shortID] = true
			hashToShortID[hashStr] = shortID

			// Preparar conteúdo para SQL (escapar aspas)
			content := strings.ReplaceAll(string(v), "'", "''")

			// Gerar timestamp (usar timestamp atual para todos)
			timestamp := time.Now().Unix()

			// Escrever comando INSERT
			insertSQL := fmt.Sprintf("INSERT OR IGNORE INTO pastes (short_id, content, content_hash, created_at) VALUES ('%s', '%s', '%s', %d);\n",
				shortID, content, hashStr, timestamp)

			_, err := sqlFile.WriteString(insertSQL)
			if err != nil {
				return fmt.Errorf("erro ao escrever SQL: %v", err)
			}

			insertCount++

			// Progresso a cada 100 pastes
			if totalPastes%100 == 0 {
				fmt.Printf("Processados %d pastes...\n", totalPastes)
			}

			return nil
		})
	})

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

	// Escrever comentários finais
	sqlFile.WriteString(fmt.Sprintf(`
-- Resumo da migração:
-- Total de pastes processados: %d
-- Pastes únicos inseridos: %d
-- Duplicatas ignoradas: %d
-- Arquivo gerado em: %s
`, totalPastes, insertCount, duplicates, time.Now().Format("02/01/2006 15:04:05")))

	fmt.Printf("\n=== Migração concluída! ===\n")
	fmt.Printf("Total de pastes processados: %d\n", totalPastes)
	fmt.Printf("Pastes únicos inseridos: %d\n", insertCount)
	fmt.Printf("Duplicatas ignoradas: %d\n", duplicates)
	fmt.Printf("Arquivo SQL gerado: %s\n", sqlOutputPath)
	fmt.Printf("\nPara aplicar a migração:\n")
	fmt.Printf("sqlite3 data/yasuc.sqlite3 < %s\n", sqlOutputPath)
}
