package main

import (
	"bytes"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/spf13/viper"
)

func TestGenerateConfigCmd_Flags(t *testing.T) {
	cmd := generateConfigCmd()
	flags := cmd.Flags()

	lang, err := flags.GetString("lang")
	if err != nil {
		t.Fatalf("failed to get --lang flag: %v", err)
	}
	if lang != "en" {
		t.Errorf("--lang default = %q, want %q", lang, "en")
	}

	output, err := flags.GetString("output")
	if err != nil {
		t.Fatalf("failed to get --output flag: %v", err)
	}
	if output != "" {
		t.Errorf("--output default = %q, want empty string", output)
	}
}

func TestGenerateConfig_LangEn(t *testing.T) {
	cmd := generateConfigCmd()
	var buf bytes.Buffer
	cmd.SetOut(&buf)
	cmd.SetErr(&buf)

	if err := cmd.Flags().Set("lang", "en"); err != nil {
		t.Fatalf("Set flag failed: %v", err)
	}

	if err := cmd.Execute(); err != nil {
		t.Fatalf("Execute failed: %v", err)
	}

	out := buf.String()
	if !strings.Contains(out, "pg:") {
		t.Errorf("output missing pg section")
	}
	if !strings.Contains(out, "host") {
		t.Errorf("output missing host key")
	}
	if !strings.Contains(out, "PostgreSQL") {
		t.Errorf("output missing English comment")
	}
}

func TestGenerateConfig_LangRu(t *testing.T) {
	cmd := generateConfigCmd()
	var buf bytes.Buffer
	cmd.SetOut(&buf)
	cmd.SetErr(&buf)

	if err := cmd.Flags().Set("lang", "ru"); err != nil {
		t.Fatalf("Set flag failed: %v", err)
	}

	if err := cmd.Execute(); err != nil {
		t.Fatalf("Execute failed: %v", err)
	}

	out := buf.String()
	if !strings.Contains(out, "pg:") {
		t.Errorf("output missing pg section")
	}
	if !strings.Contains(out, "хост") {
		t.Errorf("output missing Russian comment (хост)")
	}
}

func TestGenerateConfig_OutputFile(t *testing.T) {
	tempDir := t.TempDir()
	outputPath := filepath.Join(tempDir, "config.yaml")

	cmd := generateConfigCmd()
	var buf bytes.Buffer
	cmd.SetOut(&buf)
	cmd.SetErr(&buf)

	if err := cmd.Flags().Set("output", outputPath); err != nil {
		t.Fatalf("Set flag failed: %v", err)
	}

	if err := cmd.Execute(); err != nil {
		t.Fatalf("Execute failed: %v", err)
	}

	data, err := os.ReadFile(outputPath)
	if err != nil {
		t.Fatalf("failed to read output file: %v", err)
	}
	if len(data) == 0 {
		t.Errorf("output file is empty")
	}

	if buf.Len() != 0 {
		t.Errorf("stdout not empty when --output set: %q", buf.String())
	}
}

func TestGenerateConfig_Stdout(t *testing.T) {
	cmd := generateConfigCmd()
	var buf bytes.Buffer
	cmd.SetOut(&buf)
	cmd.SetErr(&buf)

	if err := cmd.Execute(); err != nil {
		t.Fatalf("Execute failed: %v", err)
	}

	out := buf.String()
	if out == "" {
		t.Errorf("stdout empty when no --output")
	}
	if !strings.Contains(out, "pg:") {
		t.Errorf("stdout missing pg section")
	}
}

func TestGenerateConfig_AllKeys(t *testing.T) {
	cmd := generateConfigCmd()
	var buf bytes.Buffer
	cmd.SetOut(&buf)
	cmd.SetErr(&buf)

	if err := cmd.Execute(); err != nil {
		t.Fatalf("Execute failed: %v", err)
	}

	out := buf.String()
	requiredKeys := make([]string, 0, 16)
	requiredKeys = append(requiredKeys, "host", "port", "user", "password", "database", "sslmode", "exclude_tables", "dir", "retention_days", "cron", "pq_scheme", "classical_scheme", "pq_public_key_path", "classical_public_key_path", "healthz", "log")

	for _, key := range requiredKeys {
		if !strings.Contains(out, key) {
			t.Errorf("output missing key %q", key)
		}
	}
}

func TestGenerateConfig_RoundTrip(t *testing.T) {
	tempDir := t.TempDir()
	outputPath := filepath.Join(tempDir, "config.yaml")

	cmd := generateConfigCmd()
	if err := cmd.Flags().Set("output", outputPath); err != nil {
		t.Fatalf("Set flag failed: %v", err)
	}
	if err := cmd.Execute(); err != nil {
		t.Fatalf("Execute failed: %v", err)
	}

	v := viper.New()
	v.SetConfigFile(outputPath)
	if err := v.ReadInConfig(); err != nil {
		t.Fatalf("viper read generated config failed: %v", err)
	}

	var cfg struct {
		PG struct {
			Host          string   `mapstructure:"host"`
			Port          int      `mapstructure:"port"`
			User          string   `mapstructure:"user"`
			Password      string   `mapstructure:"password"`
			Database      string   `mapstructure:"database"`
			SSLMode       string   `mapstructure:"sslmode"`
			ExcludeTables []string `mapstructure:"exclude_tables"`
		} `mapstructure:"pg"`
		Backup struct {
			Dir           string `mapstructure:"dir"`
			RetentionDays int    `mapstructure:"retention_days"`
			Cron          string `mapstructure:"cron"`
		} `mapstructure:"backup"`
		PQScheme               uint16 `mapstructure:"pq_scheme"`
		ClassicalScheme        uint16 `mapstructure:"classical_scheme"`
		PQPublicKeyPath        string `mapstructure:"pq_public_key_path"`
		ClassicalPublicKeyPath string `mapstructure:"classical_public_key_path"`
		Healthz                struct {
			Port int `mapstructure:"port"`
		} `mapstructure:"healthz"`
		Log struct {
			Level string `mapstructure:"level"`
		} `mapstructure:"log"`
	}

	if err := v.Unmarshal(&cfg); err != nil {
		t.Fatalf("viper unmarshal generated config failed: %v", err)
	}

	if cfg.PG.Port != 5432 {
		t.Errorf("PG.Port = %d, want 5432", cfg.PG.Port)
	}
	if cfg.PG.SSLMode != "prefer" {
		t.Errorf("PG.SSLMode = %q, want prefer", cfg.PG.SSLMode)
	}
	if len(cfg.PG.ExcludeTables) != 1 || cfg.PG.ExcludeTables[0] != "e2e_one_time_keys_json" {
		t.Errorf("PG.ExcludeTables = %v, want [e2e_one_time_keys_json]", cfg.PG.ExcludeTables)
	}
	if cfg.Backup.RetentionDays != 180 {
		t.Errorf("Backup.RetentionDays = %d, want 180", cfg.Backup.RetentionDays)
	}
	if cfg.Backup.Cron != "0 0 3 * * *" {
		t.Errorf("Backup.Cron = %q, want 0 0 3 * * *", cfg.Backup.Cron)
	}
	if cfg.PQScheme != 0x0006 {
		t.Errorf("PQScheme = 0x%04x, want 0x%04x", cfg.PQScheme, 0x0006)
	}
	if cfg.ClassicalScheme != 0x0007 {
		t.Errorf("ClassicalScheme = 0x%04x, want 0x%04x", cfg.ClassicalScheme, 0x0007)
	}
	if cfg.Healthz.Port != 8080 {
		t.Errorf("Healthz.Port = %d, want 8080", cfg.Healthz.Port)
	}
	if cfg.Log.Level != "info" {
		t.Errorf("Log.Level = %q, want info", cfg.Log.Level)
	}
}
