package cmd

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"io"
	"os"

	"github.com/spf13/cobra"
)

var encryptCmd = &cobra.Command{
	Use:   "encrypt",
	Short: "Зашифровать конфигурационный файл",
	Long:  `Зашифровывает YAML конфигурационный файл с помощью AES-GCM шифрования`,
	PreRunE: func(cmd *cobra.Command, args []string) error {
		// Для команды encrypt config не обязателен если есть -i
		return nil
	},
	RunE: runEncrypt,
}

func init() {
	rootCmd.AddCommand(encryptCmd)
	encryptCmd.Flags().StringVarP(&inputFile, "input", "i", "", "Входной файл для шифрования (если не указан, используется --config)")
	encryptCmd.Flags().StringVarP(&outputFile, "output", "o", "", "Выходной зашифрованный файл")
	encryptCmd.MarkFlagRequired("output")
}

var (
	inputFile  string
	outputFile string
)

func runEncrypt(cmd *cobra.Command, args []string) error {
	// Определяем входной файл: либо из -i, либо из глобального --config
	input := inputFile
	if input == "" {
		input = configFile
		if input == "" {
			return fmt.Errorf("необходимо указать входной файл через -i или --config")
		}
	}

	// Читаем входной файл
	data, err := os.ReadFile(input)
	if err != nil {
		return fmt.Errorf("не удалось прочитать входной файл %s: %w", input, err)
	}

	// Получаем ключ шифрования
	key, err := getEncryptionKeyHashed(keyFile)
	if err != nil {
		return fmt.Errorf("не удалось получить ключ шифрования: %w", err)
	}

	// Шифруем данные
	encryptedData, err := encrypt(data, key)
	if err != nil {
		return fmt.Errorf("не удалось зашифровать данные: %w", err)
	}

	// Записываем зашифрованный файл с префиксом "ENCRYPTED:"
	output := "ENCRYPTED:" + encryptedData
	if err := os.WriteFile(outputFile, []byte(output), 0600); err != nil {
		return fmt.Errorf("не удалось записать зашифрованный файл: %w", err)
	}

	fmt.Printf("Файл успешно зашифрован: %s → %s\n", input, outputFile)
	return nil
}

// getEncryptionKeyHashed получает ключ шифрования и хеширует его до 32 байт
func getEncryptionKeyHashed(keyFile string) ([]byte, error) {
	var rawKey []byte
	var err error

	if keyFile != "" {
		// Читаем ключ из файла
		rawKey, err = os.ReadFile(keyFile)
		if err != nil {
			return nil, fmt.Errorf("не удалось прочитать файл ключа: %w", err)
		}
	} else {
		// Пытаемся получить ключ из системной переменной
		key := os.Getenv("GO_KNOCKER_SERVE_PASS")
		if key == "" {
			return nil, fmt.Errorf("ключ шифрования не найден ни в файле, ни в переменной GO_KNOCKER_SERVE_PASS")
		}
		rawKey = []byte(key)
	}

	// Хешируем ключ SHA256 чтобы получить всегда 32 байта
	hash := sha256.Sum256(rawKey)
	return hash[:], nil
}

// encrypt шифрует данные с помощью AES-GCM
func encrypt(plaintext []byte, key []byte) (string, error) {
	// Создаем AES cipher
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", fmt.Errorf("не удалось создать AES cipher: %w", err)
	}

	// Создаем GCM
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", fmt.Errorf("не удалось создать GCM: %w", err)
	}

	// Создаем nonce
	nonce := make([]byte, gcm.NonceSize())
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		return "", fmt.Errorf("не удалось создать nonce: %w", err)
	}

	// Шифруем
	ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)

	// Кодируем в base64
	return base64.StdEncoding.EncodeToString(ciphertext), nil
}
