package main

import (
	"os"
	"testing"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

// TestRootCommand tests the basic structure and flags of the root command
func TestRootCommand(t *testing.T) {
	// Reset viper for clean test state
	viper.Reset()

	tests := []struct {
		name     string
		args     []string
		wantErr  bool
		validate func(t *testing.T, cmd *cobra.Command)
	}{
		{
			name:    "help flag",
			args:    []string{"--help"},
			wantErr: false,
			validate: func(t *testing.T, cmd *cobra.Command) {
				// Help should be available
				if !cmd.HasAvailableFlags() {
					t.Error("Expected command to have available flags")
				}
			},
		},
		{
			name:    "size flag",
			args:    []string{"--size", "128"},
			wantErr: false,
			validate: func(t *testing.T, cmd *cobra.Command) {
				if viper.GetInt("size") != 128 {
					t.Errorf("Expected size=128, got %d", viper.GetInt("size"))
				}
			},
		},
		{
			name:    "format flag svg",
			args:    []string{"--format", "svg"},
			wantErr: false,
			validate: func(t *testing.T, cmd *cobra.Command) {
				if viper.GetString("format") != "svg" {
					t.Errorf("Expected format=svg, got %s", viper.GetString("format"))
				}
			},
		},
		{
			name:    "format flag png",
			args:    []string{"--format", "png"},
			wantErr: false,
			validate: func(t *testing.T, cmd *cobra.Command) {
				if viper.GetString("format") != "png" {
					t.Errorf("Expected format=png, got %s", viper.GetString("format"))
				}
			},
		},
		{
			name:    "invalid format",
			args:    []string{"--format", "invalid"},
			wantErr: true,
			validate: func(t *testing.T, cmd *cobra.Command) {
				// Should not reach here on error
			},
		},
		{
			name:    "padding flag",
			args:    []string{"--padding", "0.15"},
			wantErr: false,
			validate: func(t *testing.T, cmd *cobra.Command) {
				if viper.GetFloat64("padding") != 0.15 {
					t.Errorf("Expected padding=0.15, got %f", viper.GetFloat64("padding"))
				}
			},
		},
		{
			name:    "color-saturation flag",
			args:    []string{"--color-saturation", "0.8"},
			wantErr: false,
			validate: func(t *testing.T, cmd *cobra.Command) {
				if viper.GetFloat64("color-saturation") != 0.8 {
					t.Errorf("Expected color-saturation=0.8, got %f", viper.GetFloat64("color-saturation"))
				}
			},
		},
		{
			name:    "bg-color flag",
			args:    []string{"--bg-color", "#ffffff"},
			wantErr: false,
			validate: func(t *testing.T, cmd *cobra.Command) {
				if viper.GetString("bg-color") != "#ffffff" {
					t.Errorf("Expected bg-color=#ffffff, got %s", viper.GetString("bg-color"))
				}
			},
		},
		{
			name:    "hue-restrictions flag",
			args:    []string{"--hue-restrictions", "0,120,240"},
			wantErr: false,
			validate: func(t *testing.T, cmd *cobra.Command) {
				hues := viper.GetStringSlice("hue-restrictions")
				expected := []string{"0", "120", "240"}
				if len(hues) != len(expected) {
					t.Errorf("Expected %d hue restrictions, got %d", len(expected), len(hues))
				}
				for i, hue := range expected {
					if i >= len(hues) || hues[i] != hue {
						t.Errorf("Expected hue[%d]=%s, got %s", i, hue, hues[i])
					}
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Reset viper for each test
			viper.Reset()

			// Create a fresh root command for each test
			cmd := &cobra.Command{
				Use:   "jdenticon",
				Short: "Generate identicons from any input string",
			}

			// Re-initialize flags
			initTestFlags(cmd)

			// Set args and execute
			cmd.SetArgs(tt.args)
			err := cmd.Execute()

			if (err != nil) != tt.wantErr {
				t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if !tt.wantErr && tt.validate != nil {
				tt.validate(t, cmd)
			}
		})
	}
}

// initTestFlags initializes flags for testing (similar to root init())
func initTestFlags(cmd *cobra.Command) {
	// Basic flags shared across commands
	var formatFlag FormatFlag = FormatSVG // Default to SVG
	cmd.PersistentFlags().IntP("size", "s", 200, "Size of the identicon in pixels")
	cmd.PersistentFlags().VarP(&formatFlag, "format", "f", `Output format ("png" or "svg")`)
	cmd.PersistentFlags().Float64P("padding", "p", 0.08, "Padding as percentage of icon size (0.0-0.5)")

	// Color configuration flags
	cmd.PersistentFlags().Float64("color-saturation", 0.5, "Saturation for colored shapes (0.0-1.0)")
	cmd.PersistentFlags().Float64("grayscale-saturation", 0.0, "Saturation for grayscale shapes (0.0-1.0)")
	cmd.PersistentFlags().String("bg-color", "", "Background color (hex format, e.g., #ffffff)")

	// Advanced configuration flags
	cmd.PersistentFlags().StringSlice("hue-restrictions", nil, "Restrict hues to specific degrees (0-360), e.g., --hue-restrictions=0,120,240")
	cmd.PersistentFlags().String("color-lightness", "0.4,0.8", "Color lightness range as min,max (0.0-1.0)")
	cmd.PersistentFlags().String("grayscale-lightness", "0.3,0.9", "Grayscale lightness range as min,max (0.0-1.0)")
	cmd.PersistentFlags().Int("png-supersampling", 8, "PNG supersampling factor (1-16)")

	// Bind flags to viper
	viper.BindPFlag("size", cmd.PersistentFlags().Lookup("size"))
	viper.BindPFlag("format", cmd.PersistentFlags().Lookup("format"))
	viper.BindPFlag("padding", cmd.PersistentFlags().Lookup("padding"))
	viper.BindPFlag("color-saturation", cmd.PersistentFlags().Lookup("color-saturation"))
	viper.BindPFlag("grayscale-saturation", cmd.PersistentFlags().Lookup("grayscale-saturation"))
	viper.BindPFlag("bg-color", cmd.PersistentFlags().Lookup("bg-color"))
	viper.BindPFlag("hue-restrictions", cmd.PersistentFlags().Lookup("hue-restrictions"))
	viper.BindPFlag("color-lightness", cmd.PersistentFlags().Lookup("color-lightness"))
	viper.BindPFlag("grayscale-lightness", cmd.PersistentFlags().Lookup("grayscale-lightness"))
	viper.BindPFlag("png-supersampling", cmd.PersistentFlags().Lookup("png-supersampling"))
}

// TestPopulateConfigFromFlags tests the configuration building logic
func TestPopulateConfigFromFlags(t *testing.T) {
	tests := []struct {
		name     string
		setup    func()
		wantErr  bool
		validate func(t *testing.T, config interface{}, size int)
	}{
		{
			name: "default config",
			setup: func() {
				viper.Reset()
				viper.Set("size", 200)
				viper.Set("format", "svg")
				viper.Set("png-supersampling", 8)
			},
			wantErr: false,
			validate: func(t *testing.T, config interface{}, size int) {
				if size != 200 {
					t.Errorf("Expected size=200, got %d", size)
				}
			},
		},
		{
			name: "custom config",
			setup: func() {
				viper.Reset()
				viper.Set("size", 128)
				viper.Set("padding", 0.12)
				viper.Set("color-saturation", 0.7)
				viper.Set("bg-color", "#000000")
				viper.Set("png-supersampling", 8)
			},
			wantErr: false,
			validate: func(t *testing.T, config interface{}, size int) {
				if size != 128 {
					t.Errorf("Expected size=128, got %d", size)
				}
			},
		},
		{
			name: "invalid size",
			setup: func() {
				viper.Reset()
				viper.Set("size", -1)
			},
			wantErr:  true,
			validate: nil,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			tt.setup()

			config, size, err := populateConfigFromFlags()

			if (err != nil) != tt.wantErr {
				t.Errorf("populateConfigFromFlags() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if !tt.wantErr && tt.validate != nil {
				tt.validate(t, config, size)
			}
		})
	}
}

// TestGetFormatFromViper tests format flag validation
func TestGetFormatFromViper(t *testing.T) {
	tests := []struct {
		name        string
		formatValue string
		wantFormat  FormatFlag
		wantErr     bool
	}{
		{
			name:        "svg format",
			formatValue: "svg",
			wantFormat:  FormatSVG,
			wantErr:     false,
		},
		{
			name:        "png format",
			formatValue: "png",
			wantFormat:  FormatPNG,
			wantErr:     false,
		},
		{
			name:        "invalid format",
			formatValue: "invalid",
			wantFormat:  FormatSVG,
			wantErr:     true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			viper.Reset()
			viper.Set("format", tt.formatValue)

			format, err := getFormatFromViper()

			if (err != nil) != tt.wantErr {
				t.Errorf("getFormatFromViper() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if format != tt.wantFormat {
				t.Errorf("getFormatFromViper() = %v, want %v", format, tt.wantFormat)
			}
		})
	}
}

// TestMain sets up and tears down for tests
func TestMain(m *testing.M) {
	// Setup
	code := m.Run()

	// Teardown
	viper.Reset()

	os.Exit(code)
}
