package cmd

import (
	"encoding/base64"
	"fmt"
	"os"
	"strings"

	"crypto/aes"
	"crypto/cipher"
	"crypto/sha256"

	"github.com/spf13/cobra"
)

var decryptCmd = &cobra.Command{
	Use:   "decrypt",
	Short: "Расшифровать зашифрованный конфиг в открытый YAML",
	Long:  `Расшифровывает зашифрованный конфигурационный файл (ENCRYPTED:...) в обычный YAML-файл`,
	PreRunE: func(cmd *cobra.Command, args []string) error {
		// Для команды decrypt config не обязателен если есть -i
		return nil
	},
	RunE: runDecrypt,
}

var (
	decryptInputFile  string
	decryptOutputFile string
)

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

func runDecrypt(cmd *cobra.Command, args []string) error {
	// Определяем входной файл: либо из -i, либо из глобального --config
	input := decryptInputFile
	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)
	}

	if !strings.HasPrefix(string(data), "ENCRYPTED:") {
		return fmt.Errorf("файл %s не является зашифрованным (нет префикса ENCRYPTED:)", input)
	}

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

	decrypted, err := decryptData(data[10:], key)
	if err != nil {
		return fmt.Errorf("не удалось расшифровать данные: %w", err)
	}

	if err := os.WriteFile(decryptOutputFile, decrypted, 0600); err != nil {
		return fmt.Errorf("не удалось записать YAML-файл: %w", err)
	}

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

// getDecryptionKeyHashed получает ключ шифрования и хеширует его до 32 байт (аналогично encrypt)
func getDecryptionKeyHashed(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
}

// decryptData расшифровывает данные с помощью AES-GCM (аналогично internal)
func decryptData(encryptedData []byte, key []byte) ([]byte, error) {
	data, err := base64.StdEncoding.DecodeString(string(encryptedData))
	if err != nil {
		return nil, fmt.Errorf("не удалось декодировать base64: %w", err)
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("не удалось создать AES cipher: %w", err)
	}

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

	nonceSize := gcm.NonceSize()
	if len(data) < nonceSize {
		return nil, fmt.Errorf("данные слишком короткие")
	}

	nonce, ciphertext := data[:nonceSize], data[nonceSize:]
	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		return nil, fmt.Errorf("не удалось расшифровать: %w", err)
	}

	return plaintext, nil
}
