package service

import (
	"context"
	"sync"
	"testing"

	"{{GO_MODULE}}/pkg/logging"
	"{{GO_MODULE}}/services/{{COMPONENT_NAME}}/internal/domain"
	"{{GO_MODULE}}/services/{{COMPONENT_NAME}}/internal/port"
)

// mockExampleRepository implements port.ExampleRepository for testing.
type mockExampleRepository struct {
	mu       sync.RWMutex
	examples map[domain.ExampleID]*domain.Example
}

var _ port.ExampleRepository = (*mockExampleRepository)(nil)

func newMockExampleRepository() *mockExampleRepository {
	return &mockExampleRepository{
		examples: make(map[domain.ExampleID]*domain.Example),
	}
}

func (m *mockExampleRepository) List(ctx context.Context) ([]domain.Example, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	result := make([]domain.Example, 0, len(m.examples))
	for _, e := range m.examples {
		result = append(result, *e)
	}
	return result, nil
}

func (m *mockExampleRepository) Get(ctx context.Context, id domain.ExampleID) (*domain.Example, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	e, ok := m.examples[id]
	if !ok {
		return nil, domain.ErrExampleNotFound
	}
	// Return a copy to avoid mutation
	copy := *e
	return &copy, nil
}

func (m *mockExampleRepository) Create(ctx context.Context, example *domain.Example) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	// Store a copy
	copy := *example
	m.examples[example.ID] = &copy
	return nil
}

func (m *mockExampleRepository) Update(ctx context.Context, example *domain.Example) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	if _, ok := m.examples[example.ID]; !ok {
		return domain.ErrExampleNotFound
	}
	// Store a copy
	copy := *example
	m.examples[example.ID] = &copy
	return nil
}

func (m *mockExampleRepository) Delete(ctx context.Context, id domain.ExampleID) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	if _, ok := m.examples[id]; !ok {
		return domain.ErrExampleNotFound
	}
	delete(m.examples, id)
	return nil
}

func (m *mockExampleRepository) ExistsByName(ctx context.Context, name string) (bool, error) {
	m.mu.RLock()
	defer m.mu.RUnlock()

	for _, e := range m.examples {
		if e.Name == name {
			return true, nil
		}
	}
	return false, nil
}

func TestExampleService_Create(t *testing.T) {
	repo := newMockExampleRepository()
	svc := NewExampleService(repo, logging.Nop())

	t.Run("creates example successfully", func(t *testing.T) {
		example, err := svc.Create(context.Background(), CreateInput{
			Name:        "Test Example",
			Description: "A test description",
		})
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if example.Name != "Test Example" {
			t.Errorf("expected name 'Test Example', got '%s'", example.Name)
		}
		if example.ID.IsZero() {
			t.Error("expected non-empty ID")
		}
	})

	t.Run("rejects duplicate name", func(t *testing.T) {
		_, err := svc.Create(context.Background(), CreateInput{
			Name:        "Test Example",
			Description: "Another description",
		})
		if err != domain.ErrDuplicateExample {
			t.Errorf("expected ErrDuplicateExample, got %v", err)
		}
	})

	t.Run("rejects empty name", func(t *testing.T) {
		_, err := svc.Create(context.Background(), CreateInput{
			Name:        "",
			Description: "Description",
		})
		if err != domain.ErrInvalidExampleName {
			t.Errorf("expected ErrInvalidExampleName, got %v", err)
		}
	})
}

func TestExampleService_Get(t *testing.T) {
	repo := newMockExampleRepository()
	svc := NewExampleService(repo, logging.Nop())

	// Create an example first
	created, _ := svc.Create(context.Background(), CreateInput{
		Name:        "Get Test",
		Description: "Description",
	})

	t.Run("returns existing example", func(t *testing.T) {
		example, err := svc.Get(context.Background(), created.ID)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if example.Name != "Get Test" {
			t.Errorf("expected name 'Get Test', got '%s'", example.Name)
		}
	})

	t.Run("returns not found for missing example", func(t *testing.T) {
		_, err := svc.Get(context.Background(), "nonexistent-id")
		if err != domain.ErrExampleNotFound {
			t.Errorf("expected ErrExampleNotFound, got %v", err)
		}
	})
}

func TestExampleService_Update(t *testing.T) {
	repo := newMockExampleRepository()
	svc := NewExampleService(repo, logging.Nop())

	// Create examples
	example1, _ := svc.Create(context.Background(), CreateInput{
		Name:        "Update Test 1",
		Description: "Original",
	})
	_, _ = svc.Create(context.Background(), CreateInput{
		Name:        "Update Test 2",
		Description: "Other",
	})

	t.Run("updates example successfully", func(t *testing.T) {
		updated, err := svc.Update(context.Background(), example1.ID, UpdateInput{
			Name:        "Updated Name",
			Description: "Updated description",
		})
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if updated.Name != "Updated Name" {
			t.Errorf("expected name 'Updated Name', got '%s'", updated.Name)
		}
	})

	t.Run("allows same name on same example", func(t *testing.T) {
		_, err := svc.Update(context.Background(), example1.ID, UpdateInput{
			Name:        "Updated Name",
			Description: "Same name",
		})
		if err != nil {
			t.Errorf("unexpected error updating with same name: %v", err)
		}
	})

	t.Run("rejects name conflict", func(t *testing.T) {
		_, err := svc.Update(context.Background(), example1.ID, UpdateInput{
			Name:        "Update Test 2",
			Description: "Conflict",
		})
		if err != domain.ErrDuplicateExample {
			t.Errorf("expected ErrDuplicateExample, got %v", err)
		}
	})

	t.Run("returns not found for missing example", func(t *testing.T) {
		_, err := svc.Update(context.Background(), "nonexistent-id", UpdateInput{
			Name:        "Anything",
			Description: "",
		})
		if err != domain.ErrExampleNotFound {
			t.Errorf("expected ErrExampleNotFound, got %v", err)
		}
	})
}

func TestExampleService_Delete(t *testing.T) {
	repo := newMockExampleRepository()
	svc := NewExampleService(repo, logging.Nop())

	// Create an example first
	created, _ := svc.Create(context.Background(), CreateInput{
		Name:        "Delete Test",
		Description: "To be deleted",
	})

	t.Run("deletes example successfully", func(t *testing.T) {
		err := svc.Delete(context.Background(), created.ID)
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}

		// Verify deleted
		_, err = svc.Get(context.Background(), created.ID)
		if err != domain.ErrExampleNotFound {
			t.Errorf("expected ErrExampleNotFound after delete, got %v", err)
		}
	})

	t.Run("returns not found for missing example", func(t *testing.T) {
		err := svc.Delete(context.Background(), "nonexistent-id")
		if err != domain.ErrExampleNotFound {
			t.Errorf("expected ErrExampleNotFound, got %v", err)
		}
	})
}

func TestExampleService_List(t *testing.T) {
	repo := newMockExampleRepository()
	svc := NewExampleService(repo, logging.Nop())

	t.Run("returns empty list initially", func(t *testing.T) {
		examples, err := svc.List(context.Background())
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if len(examples) != 0 {
			t.Errorf("expected 0 examples, got %d", len(examples))
		}
	})

	// Create some examples
	_, _ = svc.Create(context.Background(), CreateInput{Name: "List Test 1", Description: ""})
	_, _ = svc.Create(context.Background(), CreateInput{Name: "List Test 2", Description: ""})

	t.Run("returns all examples", func(t *testing.T) {
		examples, err := svc.List(context.Background())
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if len(examples) != 2 {
			t.Errorf("expected 2 examples, got %d", len(examples))
		}
	})
}
