package crypto

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

// BackfillEncryptedKeys encrypts any remaining plaintext API keys in
// provider_configs.api_key_plain using the env-derived key, writing the
// result to api_key_enc (BYTEA) + key_nonce. Clears api_key_plain after.
//
// This runs once on upgrade from v0.9.3 → v0.9.4. Subsequent starts are
// a no-op (api_key_plain will be NULL for all rows).
//
// Personal-scope keys are temporarily encrypted with the env key. They get
// re-encrypted with the user's UEK on next login (see VaultUpgradeOnLogin).
func BackfillEncryptedKeys(db *sql.DB, encryptionKey string) error {
	envKey, err := DeriveKeyFromEnv(encryptionKey)
	if err != nil {
		return fmt.Errorf("vault backfill: %w", err)
	}

	// Check if api_key_plain column exists (it won't on fresh installs after
	// the column is eventually dropped)
	var colExists bool
	db.QueryRow(`
		SELECT EXISTS (
			SELECT 1 FROM information_schema.columns
			WHERE table_name = 'provider_configs' AND column_name = 'api_key_plain'
		)
	`).Scan(&colExists)
	if !colExists {
		return nil // Column already dropped — nothing to backfill
	}

	// Find rows with plaintext keys that haven't been encrypted yet
	rows, err := db.Query(`
		SELECT id, api_key_plain
		FROM provider_configs
		WHERE api_key_plain IS NOT NULL
		  AND api_key_plain != ''
		  AND api_key_enc IS NULL
	`)
	if err != nil {
		return fmt.Errorf("vault backfill query: %w", err)
	}
	defer rows.Close()

	var count int
	for rows.Next() {
		var id, plaintext string
		if err := rows.Scan(&id, &plaintext); err != nil {
			return fmt.Errorf("vault backfill scan: %w", err)
		}

		ciphertext, nonce, err := Encrypt(plaintext, envKey)
		if err != nil {
			return fmt.Errorf("vault backfill encrypt %s: %w", id, err)
		}

		_, err = db.Exec(`
			UPDATE provider_configs
			SET api_key_enc = $1, key_nonce = $2, api_key_plain = NULL
			WHERE id = $3
		`, ciphertext, nonce, id)
		if err != nil {
			return fmt.Errorf("vault backfill update %s: %w", id, err)
		}
		count++
	}

	if count > 0 {
		log.Printf("  🔐 Vault backfill: encrypted %d API key(s)", count)
	}
	return nil
}

// EnforceEncryptionKey checks startup preconditions:
//   - If encrypted keys exist in DB and ENCRYPTION_KEY is empty → fatal.
//   - If no keys exist and ENCRYPTION_KEY is empty → warn (fresh install OK).
func EnforceEncryptionKey(db *sql.DB, encryptionKey string) error {
	if db == nil {
		return nil // No DB, no keys to protect
	}

	// Check if any encrypted keys exist
	var hasEncrypted bool
	db.QueryRow(`
		SELECT EXISTS (
			SELECT 1 FROM provider_configs WHERE api_key_enc IS NOT NULL
		)
	`).Scan(&hasEncrypted)

	// Check if any plaintext keys exist (pre-backfill)
	var hasPlaintext bool
	// api_key_plain column may not exist on fresh v0.9.4+ installs
	var colExists bool
	db.QueryRow(`
		SELECT EXISTS (
			SELECT 1 FROM information_schema.columns
			WHERE table_name = 'provider_configs' AND column_name = 'api_key_plain'
		)
	`).Scan(&colExists)
	if colExists {
		db.QueryRow(`
			SELECT EXISTS (
				SELECT 1 FROM provider_configs
				WHERE api_key_plain IS NOT NULL AND api_key_plain != ''
			)
		`).Scan(&hasPlaintext)
	}

	if encryptionKey == "" {
		if hasEncrypted {
			return fmt.Errorf(
				"ENCRYPTION_KEY is not set but encrypted API keys exist in the database. " +
					"Set ENCRYPTION_KEY to the value used when keys were encrypted, or data will be unrecoverable",
			)
		}
		if hasPlaintext {
			return fmt.Errorf(
				"ENCRYPTION_KEY is not set but plaintext API keys need encryption. " +
					"Set ENCRYPTION_KEY before starting (required for v0.9.4+)",
			)
		}
		log.Println("  ⚠ ENCRYPTION_KEY not set — API key encryption disabled (OK for fresh install with no providers)")
	}

	return nil
}
