package channel

import (
	"testing"
)

func TestCreateChannel(t *testing.T) {
	tests := []struct {
		name      string
		chName    string
		ownerID   string
		wantErr   bool
		errType   error
	}{
		{
			name:    "create valid channel",
			chName:  "general",
			ownerID: "user-1",
			wantErr: false,
		},
		{
			name:    "empty channel name",
			chName:  "",
			ownerID: "user-1",
			wantErr: true,
			errType: ErrInvalidChannelName,
		},
		{
			name:    "channel name too short",
			chName:  "a",
			ownerID: "user-1",
			wantErr: true,
			errType: ErrInvalidChannelName,
		},
		{
			name:    "channel name too long",
			chName:  "channel-with-very-long-name-that-exceeds-fifty-characters-limit",
			ownerID: "user-1",
			wantErr: true,
			errType: ErrInvalidChannelName,
		},
		{
			name:    "duplicate channel name",
			chName:  "general",
			ownerID: "user-1",
			wantErr: false,
		},
	}

	for i, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Reset manager for duplicate test
			var m *Manager
			if i < 3 || i > 3 {
				m = NewManager()
			}

			ch, err := m.CreateChannel(tt.chName, tt.ownerID)

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

			if tt.wantErr && err != tt.errType {
				t.Errorf("CreateChannel() error = %v, expected %v", err, tt.errType)
			}

			if !tt.wantErr {
				if ch == nil {
					t.Error("CreateChannel() returned nil channel")
					return
				}

				if ch.Name != tt.chName {
					t.Errorf("Channel name = %s, want %s", ch.Name, tt.chName)
				}

				if ch.OwnerID != tt.ownerID {
					t.Errorf("Channel owner = %s, want %s", ch.OwnerID, tt.ownerID)
				}

				// Verify owner is automatically a member
				if !ch.IsMember(tt.ownerID) {
					t.Error("Owner should be automatically added as member")
				}

				if ch.Status != StatusActive {
					t.Errorf("Channel status = %v, want %v", ch.Status, StatusActive)
				}
			}
		})
	}
}

func TestDuplicateChannelName(t *testing.T) {
	m := NewManager()

	// Create first channel
	_, err := m.CreateChannel("general", "user-1")
	if err != nil {
		t.Fatalf("CreateChannel() error = %v", err)
	}

	// Try to create duplicate
	_, err = m.CreateChannel("general", "user-2")
	if err != ErrChannelAlreadyExists {
		t.Errorf("CreateChannel() duplicate error = %v, expected %v", err, ErrChannelAlreadyExists)
	}
}

func TestGetChannel(t *testing.T) {
	m := NewManager()

	ch, err := m.CreateChannel("test", "user-1")
	if err != nil {
		t.Fatalf("CreateChannel() error = %v", err)
	}

	tests := []struct {
		name      string
		channelID string
		wantErr   bool
		wantName  string
	}{
		{
			name:      "get existing channel",
			channelID: ch.ID,
			wantErr:   false,
			wantName:  "test",
		},
		{
			name:      "get nonexistent channel",
			channelID: "nonexistent-id",
			wantErr:   true,
			wantName:  "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			retrieved, err := m.GetChannel(tt.channelID)
			if (err != nil) != tt.wantErr {
				t.Errorf("GetChannel() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if !tt.wantErr && retrieved.Name != tt.wantName {
				t.Errorf("GetChannel() name = %s, want %s", retrieved.Name, tt.wantName)
			}
		})
	}
}

func TestListChannels(t *testing.T) {
	m := NewManager()

	// Create multiple channels
	m.CreateChannel("general", "user-1")
	m.CreateChannel("announcements", "user-2")
	m.CreateChannel("off-topic", "user-1")

	channels := m.ListChannels()
	if len(channels) != 3 {
		t.Errorf("ListChannels() returned %d channels, want 3", len(channels))
	}
}

func TestJoinChannel(t *testing.T) {
	m := NewManager()
	ch, _ := m.CreateChannel("test", "user-1")

	tests := []struct {
		name      string
		userID    string
		channelID string
		wantErr   bool
	}{
		{
			name:      "join existing channel",
			userID:    "user-2",
			channelID: ch.ID,
			wantErr:   false,
		},
		{
			name:      "join nonexistent channel",
			userID:    "user-3",
			channelID: "nonexistent",
			wantErr:   true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			_, err := m.JoinChannel(tt.channelID, tt.userID)
			if (err != nil) != tt.wantErr {
				t.Errorf("JoinChannel() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if !tt.wantErr {
				channel, _ := m.GetChannel(tt.channelID)
				if !channel.IsMember(tt.userID) {
					t.Errorf("User %s not in channel after join", tt.userID)
				}
			}
		})
	}
}

func TestLeaveChannel(t *testing.T) {
	m := NewManager()
	ch, _ := m.CreateChannel("test", "user-1")

	// User joins
	m.JoinChannel(ch.ID, "user-2")

	// User leaves
	err := m.LeaveChannel(ch.ID, "user-2")
	if err != nil {
		t.Errorf("LeaveChannel() error = %v", err)
		return
	}

	// Verify user is no longer member
	channel, _ := m.GetChannel(ch.ID)
	if channel.IsMember("user-2") {
		t.Error("User should not be member after leaving")
	}
}

func TestChannelCapacity(t *testing.T) {
	m := NewManager()
	ch, _ := m.CreateChannel("test", "user-1")
	ch.MaxUsers = 2

	// User 1 (owner) already member, join user 2
	m.JoinChannel(ch.ID, "user-2")

	// Try to add user 3 (should fail - at capacity)
	_, err := m.JoinChannel(ch.ID, "user-3")
	if err != ErrChannelFull {
		t.Errorf("JoinChannel() capacity error = %v, expected %v", err, ErrChannelFull)
	}
}

func TestDeleteChannel(t *testing.T) {
	m := NewManager()
	ch, _ := m.CreateChannel("test", "user-1")

	// Soft delete
	err := m.DeleteChannel(ch.ID, false)
	if err != nil {
		t.Errorf("DeleteChannel() soft error = %v", err)
	}

	// Verify it's archived
	channel, _ := m.GetChannel(ch.ID)
	if channel.Status != StatusArchived {
		t.Errorf("Channel status = %v, want %v", channel.Status, StatusArchived)
	}

	// Hard delete
	err = m.DeleteChannel(ch.ID, true)
	if err != nil {
		t.Errorf("DeleteChannel() hard error = %v", err)
	}

	// Verify it's gone
	_, err = m.GetChannel(ch.ID)
	if err != ErrChannelNotFound {
		t.Errorf("GetChannel() after hard delete error = %v, expected %v", err, ErrChannelNotFound)
	}
}

func TestGetChannelMembers(t *testing.T) {
	m := NewManager()
	ch, _ := m.CreateChannel("test", "user-1")

	m.JoinChannel(ch.ID, "user-2")
	m.JoinChannel(ch.ID, "user-3")

	members, err := m.GetChannelMembers(ch.ID)
	if err != nil {
		t.Errorf("GetChannelMembers() error = %v", err)
		return
	}

	if len(members) != 3 {
		t.Errorf("GetChannelMembers() returned %d members, want 3", len(members))
	}
}

func TestUpdateChannel(t *testing.T) {
	m := NewManager()

	tests := []struct {
		name        string
		newName     string
		description string
		isPublic    bool
		wantErr     bool
		setup       func(*Manager) string
	}{
		{
			name:        "update all fields",
			newName:     "updated",
			description: "New description",
			isPublic:    false,
			wantErr:     false,
			setup: func(m *Manager) string {
				ch, _ := m.CreateChannel("test", "user-1")
				return ch.ID
			},
		},
		{
			name:    "duplicate new name",
			newName: "other",
			wantErr: true,
			setup: func(m *Manager) string {
				ch, _ := m.CreateChannel("test2", "user-1")
				m.CreateChannel("other", "user-2")
				return ch.ID
			},
		},
	}

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

			updated, err := m.UpdateChannel(channelID, tt.newName, tt.description, tt.isPublic, 10)
			if (err != nil) != tt.wantErr {
				t.Errorf("UpdateChannel() error = %v, wantErr %v", err, tt.wantErr)
				return
			}

			if !tt.wantErr && updated.Name != tt.newName {
				t.Errorf("Updated name = %s, want %s", updated.Name, tt.newName)
			}
		})
	}
}

func TestIsUserInChannel(t *testing.T) {
	m := NewManager()
	ch, _ := m.CreateChannel("test", "user-1")

	if !m.IsUserInChannel(ch.ID, "user-1") {
		t.Error("Owner should be in channel")
	}

	if m.IsUserInChannel(ch.ID, "user-2") {
		t.Error("User-2 should not be in channel yet")
	}

	m.JoinChannel(ch.ID, "user-2")

	if !m.IsUserInChannel(ch.ID, "user-2") {
		t.Error("User-2 should be in channel after joining")
	}
}

func TestGetUserChannels(t *testing.T) {
	m := NewManager()

	_, _ = m.CreateChannel("general", "user-1")
	ch2, _ := m.CreateChannel("announcements", "user-2")
	_, _ = m.CreateChannel("off-topic", "user-1")

	m.JoinChannel(ch2.ID, "user-1")

	userChannels := m.GetUserChannels("user-1")
	if len(userChannels) != 3 {
		t.Errorf("GetUserChannels() returned %d channels, want 3", len(userChannels))
	}

	userChannels = m.GetUserChannels("user-2")
	if len(userChannels) != 1 {
		t.Errorf("GetUserChannels() returned %d channels for user-2, want 1", len(userChannels))
	}
}

func TestChannelConcurrency(t *testing.T) {
	m := NewManager()
	ch, _ := m.CreateChannel("test", "user-1")

	// Multiple users join concurrently
	done := make(chan bool, 100)
	for i := 0; i < 100; i++ {
		go func(userID string) {
			m.JoinChannel(ch.ID, userID)
			done <- true
		}(("user-" + string(rune(i))))
	}

	// Wait for all goroutines
	for i := 0; i < 100; i++ {
		<-done
	}

	// Owner + 100 concurrent users
	members, _ := m.GetChannelMembers(ch.ID)
	if len(members) < 1 {
		t.Errorf("Expected at least 1 member, got %d", len(members))
	}
}
