package imap

import (
	"log/slog"
	"testing"

	"code.chimeric.al/chimerical/raven/internal/filter"
)

func TestConfigAddress(t *testing.T) {
	tests := []struct {
		name string
		cfg  Config
		want string
	}{
		{
			name: "standard config",
			cfg:  Config{Host: "imap.example.com", Port: "993"},
			want: "imap.example.com:993",
		},
		{
			name: "empty host",
			cfg:  Config{Host: "", Port: "993"},
			want: ":993",
		},
		{
			name: "empty port",
			cfg:  Config{Host: "imap.example.com", Port: ""},
			want: "imap.example.com:",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := tt.cfg.Address(); got != tt.want {
				t.Errorf(
					"Address() = %q, want %q",
					got, tt.want,
				)
			}
		})
	}
}

func TestConfigValidate(t *testing.T) {
	tests := []struct {
		name    string
		cfg     Config
		wantErr string
	}{
		{
			name: "valid config",
			cfg: Config{
				Host:     "imap.example.com",
				Password: "secret",
				Port:     "993",
				User:     "user@example.com",
			},
			wantErr: "",
		},
		{
			name: "missing host",
			cfg: Config{
				Password: "secret",
				Port:     "993",
				User:     "user@example.com",
			},
			wantErr: "missing host",
		},
		{
			name: "missing password",
			cfg: Config{
				Host: "imap.example.com",
				Port: "993",
				User: "user@example.com",
			},
			wantErr: "missing password",
		},
		{
			name: "missing port",
			cfg: Config{
				Host:     "imap.example.com",
				Password: "secret",
				User:     "user@example.com",
			},
			wantErr: "missing port",
		},
		{
			name: "missing user",
			cfg: Config{
				Host:     "imap.example.com",
				Password: "secret",
				Port:     "993",
			},
			wantErr: "missing user",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := tt.cfg.Validate()
			if tt.wantErr == "" {
				if err != nil {
					t.Errorf(
						"Validate() unexpected error: %v",
						err,
					)
				}
				return
			}
			if err == nil {
				t.Errorf(
					"Validate() expected error containing %q, got nil",
					tt.wantErr,
				)
				return
			}
			if got := err.Error(); got != tt.wantErr {
				t.Errorf(
					"Validate() error = %q, want %q",
					got, tt.wantErr,
				)
			}
		})
	}
}

func TestNewClient(t *testing.T) {
	validCfg := Config{
		Host:     "imap.example.com",
		Password: "secret",
		Port:     "993",
		User:     "user@example.com",
	}

	tests := []struct {
		name    string
		cfg     Config
		filters filter.Filters
		wantErr bool
	}{
		{
			name:    "valid config",
			cfg:     validCfg,
			filters: filter.Filters{},
			wantErr: false,
		},
		{
			name: "valid config with filters",
			cfg:  validCfg,
			filters: filter.Filters{
				Subject: "test",
				To:      "recipient@example.com",
				Body:    []string{"keyword"},
			},
			wantErr: false,
		},
		{
			name:    "invalid config",
			cfg:     Config{Host: "imap.example.com"},
			filters: filter.Filters{},
			wantErr: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			client, err := NewClient(
				tt.cfg, tt.filters, slog.Default(),
			)
			if tt.wantErr {
				if err == nil {
					t.Error(
						"NewClient() expected error, got nil",
					)
				}
				return
			}
			if err != nil {
				t.Errorf(
					"NewClient() unexpected error: %v", err,
				)
				return
			}
			if client == nil {
				t.Error("NewClient() returned nil client")
				return
			}
			// Verify client is not connected.
			if client.Client() != nil {
				t.Error(
					"NewClient() client should not be connected",
				)
			}
		})
	}
}

func TestClientNotConnected(t *testing.T) {
	cfg := Config{
		Host:     "imap.example.com",
		Password: "secret",
		Port:     "993",
		User:     "user@example.com",
	}
	client, err := NewClient(cfg, filter.Filters{}, slog.Default())
	if err != nil {
		t.Fatalf("NewClient() unexpected error: %v", err)
	}

	t.Run("Fetch without connection", func(t *testing.T) {
		_, err := client.Fetch(1, true)
		if err == nil {
			t.Error("Fetch() expected error when not connected")
		}
	})

	t.Run("MarkSeen without connection", func(t *testing.T) {
		err := client.MarkSeen(1)
		if err == nil {
			t.Error("MarkSeen() expected error when not connected")
		}
	})
}
