package orgconfig

import (
	"context"
	"testing"

	. "github.com/onsi/gomega"

	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/domain/repository"
)

// mockOrgRuConfigRepo implements repository.OrganizationRuConfigRepository for testing.
type mockOrgRuConfigRepo struct {
	configs map[uint64]*entity.OrganizationRuConfig
	err     error
}

func newMockRepo() *mockOrgRuConfigRepo {
	return &mockOrgRuConfigRepo{
		configs: make(map[uint64]*entity.OrganizationRuConfig),
	}
}

func (m *mockOrgRuConfigRepo) GetByOrganizationId(_ context.Context, organizationId uint64) (*entity.OrganizationRuConfig, error) {
	if m.err != nil {
		return nil, m.err
	}
	config, ok := m.configs[organizationId]
	if !ok {
		return &entity.OrganizationRuConfig{
			OrganizationID:   organizationId,
			DefaultRuVersion: repository.SystemDefaultRuVersion,
		}, nil
	}
	return config, nil
}

func (m *mockOrgRuConfigRepo) Upsert(_ context.Context, config *entity.OrganizationRuConfig) error {
	if m.err != nil {
		return m.err
	}
	m.configs[config.OrganizationID] = config
	return nil
}

func TestUpdateOrganizationDefaultRuVersion(t *testing.T) {
	t.Run("should succeed with valid request", func(t *testing.T) {
		g := NewWithT(t)

		repo := newMockRepo()
		srv := NewServer(repo)

		resp, err := srv.UpdateOrganizationDefaultRuVersion(context.Background(), &tidbmgmtservicev1.UpdateOrganizationDefaultRuVersionRequest{
			OrganizationId:   12345,
			DefaultRuVersion: 3,
		})

		g.Expect(err).ShouldNot(HaveOccurred())
		g.Expect(resp).ShouldNot(BeNil())
		g.Expect(resp.OrganizationId).Should(Equal(uint64(12345)))
		g.Expect(resp.DefaultRuVersion).Should(Equal(int32(3)))

		// Verify it was persisted
		stored := repo.configs[12345]
		g.Expect(stored).ShouldNot(BeNil())
		g.Expect(stored.DefaultRuVersion).Should(Equal(int32(3)))
	})

	t.Run("should update existing config", func(t *testing.T) {
		g := NewWithT(t)

		repo := newMockRepo()
		repo.configs[12345] = &entity.OrganizationRuConfig{
			OrganizationID:   12345,
			DefaultRuVersion: 2,
		}
		srv := NewServer(repo)

		resp, err := srv.UpdateOrganizationDefaultRuVersion(context.Background(), &tidbmgmtservicev1.UpdateOrganizationDefaultRuVersionRequest{
			OrganizationId:   12345,
			DefaultRuVersion: 5,
		})

		g.Expect(err).ShouldNot(HaveOccurred())
		g.Expect(resp.DefaultRuVersion).Should(Equal(int32(5)))
		g.Expect(repo.configs[12345].DefaultRuVersion).Should(Equal(int32(5)))
	})

	t.Run("should fail with zero organization_id", func(t *testing.T) {
		g := NewWithT(t)

		repo := newMockRepo()
		srv := NewServer(repo)

		resp, err := srv.UpdateOrganizationDefaultRuVersion(context.Background(), &tidbmgmtservicev1.UpdateOrganizationDefaultRuVersionRequest{
			OrganizationId:   0,
			DefaultRuVersion: 3,
		})

		g.Expect(err).Should(HaveOccurred())
		g.Expect(resp).Should(BeNil())
		g.Expect(err.Error()).Should(ContainSubstring("organization_id is required"))
	})

	t.Run("should fail with non-positive ru_version", func(t *testing.T) {
		g := NewWithT(t)

		repo := newMockRepo()
		srv := NewServer(repo)

		resp, err := srv.UpdateOrganizationDefaultRuVersion(context.Background(), &tidbmgmtservicev1.UpdateOrganizationDefaultRuVersionRequest{
			OrganizationId:   12345,
			DefaultRuVersion: 0,
		})

		g.Expect(err).Should(HaveOccurred())
		g.Expect(resp).Should(BeNil())
		g.Expect(err.Error()).Should(ContainSubstring("default_ru_version must be a positive integer"))
	})

	t.Run("should fail with negative ru_version", func(t *testing.T) {
		g := NewWithT(t)

		repo := newMockRepo()
		srv := NewServer(repo)

		resp, err := srv.UpdateOrganizationDefaultRuVersion(context.Background(), &tidbmgmtservicev1.UpdateOrganizationDefaultRuVersionRequest{
			OrganizationId:   12345,
			DefaultRuVersion: -1,
		})

		g.Expect(err).Should(HaveOccurred())
		g.Expect(resp).Should(BeNil())
	})
}

func TestGetOrganizationDefaultRuVersion(t *testing.T) {
	t.Run("should return system default when no config exists", func(t *testing.T) {
		g := NewWithT(t)

		repo := newMockRepo()
		srv := NewServer(repo)

		resp, err := srv.GetOrganizationDefaultRuVersion(context.Background(), &tidbmgmtservicev1.GetOrganizationDefaultRuVersionRequest{
			OrganizationId: 12345,
		})

		g.Expect(err).ShouldNot(HaveOccurred())
		g.Expect(resp).ShouldNot(BeNil())
		g.Expect(resp.OrganizationId).Should(Equal(uint64(12345)))
		g.Expect(resp.DefaultRuVersion).Should(Equal(repository.SystemDefaultRuVersion))
	})

	t.Run("should return stored config when exists", func(t *testing.T) {
		g := NewWithT(t)

		repo := newMockRepo()
		repo.configs[12345] = &entity.OrganizationRuConfig{
			OrganizationID:   12345,
			DefaultRuVersion: 5,
		}
		srv := NewServer(repo)

		resp, err := srv.GetOrganizationDefaultRuVersion(context.Background(), &tidbmgmtservicev1.GetOrganizationDefaultRuVersionRequest{
			OrganizationId: 12345,
		})

		g.Expect(err).ShouldNot(HaveOccurred())
		g.Expect(resp).ShouldNot(BeNil())
		g.Expect(resp.OrganizationId).Should(Equal(uint64(12345)))
		g.Expect(resp.DefaultRuVersion).Should(Equal(int32(5)))
	})

	t.Run("should fail with zero organization_id", func(t *testing.T) {
		g := NewWithT(t)

		repo := newMockRepo()
		srv := NewServer(repo)

		resp, err := srv.GetOrganizationDefaultRuVersion(context.Background(), &tidbmgmtservicev1.GetOrganizationDefaultRuVersionRequest{
			OrganizationId: 0,
		})

		g.Expect(err).Should(HaveOccurred())
		g.Expect(resp).Should(BeNil())
		g.Expect(err.Error()).Should(ContainSubstring("organization_id is required"))
	})

	t.Run("should propagate repo error", func(t *testing.T) {
		g := NewWithT(t)

		repo := newMockRepo()
		repo.err = context.DeadlineExceeded
		srv := NewServer(repo)

		resp, err := srv.GetOrganizationDefaultRuVersion(context.Background(), &tidbmgmtservicev1.GetOrganizationDefaultRuVersionRequest{
			OrganizationId: 12345,
		})

		g.Expect(err).Should(HaveOccurred())
		g.Expect(resp).Should(BeNil())
	})
}

func TestUpdateThenGet(t *testing.T) {
	t.Run("update then get should return updated value", func(t *testing.T) {
		g := NewWithT(t)

		repo := newMockRepo()
		srv := NewServer(repo)

		// First, get should return system default
		resp, err := srv.GetOrganizationDefaultRuVersion(context.Background(), &tidbmgmtservicev1.GetOrganizationDefaultRuVersionRequest{
			OrganizationId: 99999,
		})
		g.Expect(err).ShouldNot(HaveOccurred())
		g.Expect(resp.DefaultRuVersion).Should(Equal(repository.SystemDefaultRuVersion))

		// Update
		_, err = srv.UpdateOrganizationDefaultRuVersion(context.Background(), &tidbmgmtservicev1.UpdateOrganizationDefaultRuVersionRequest{
			OrganizationId:   99999,
			DefaultRuVersion: 7,
		})
		g.Expect(err).ShouldNot(HaveOccurred())

		// Get should now return updated value
		resp, err = srv.GetOrganizationDefaultRuVersion(context.Background(), &tidbmgmtservicev1.GetOrganizationDefaultRuVersionRequest{
			OrganizationId: 99999,
		})
		g.Expect(err).ShouldNot(HaveOccurred())
		g.Expect(resp.DefaultRuVersion).Should(Equal(int32(7)))
	})
}
