package audit

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
	devopsapi "github.com/tidbcloud/devops/api"
	auditapi "github.com/tidbcloud/devops/api/audit"
	auditent "github.com/tidbcloud/devops/entity/audit"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	testutil "github.com/tidbcloud/devops/pkg/infra/utils/test"
	auditsvc "github.com/tidbcloud/devops/pkg/service/audit"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
)

func TestAuditController(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Audit Controller Suite", testutil.ReporterConfig())
}

var _ = Describe("[AuditController]", func() {
	var (
		router   *gin.Engine
		recorder *httptest.ResponseRecorder
		fakeSvc  *fakeAuditService
	)

	BeforeEach(func() {
		gin.SetMode(gin.TestMode)
		router = gin.New()
		recorder = httptest.NewRecorder()

		fakeSvc = &fakeAuditService{}
		c := &AuditController{auditSvc: fakeSvc}
		err := c.RegisterAPI(&ctrl.Suite{Router: router.Group("/api/v1/devops")})
		Expect(err).To(BeNil())
	})

	It("GET /audit/types returns all audit log types", func() {
		fakeSvc.types = append([]auditent.AuditLogType(nil), auditent.AuditLogTypeValues...)

		req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/api/v1/devops/audit/types", nil)
		Expect(err).To(BeNil())

		router.ServeHTTP(recorder, req)
		Expect(recorder.Code).To(Equal(http.StatusOK))

		resp := &auditapi.ListAuditLogTypesResp{}
		err = json.Unmarshal(recorder.Body.Bytes(), resp)
		Expect(err).To(BeNil())
		Expect(resp.ErrCode).To(Equal(devopsapi.ErrCodeOK))
		Expect(resp.Types).To(Equal(auditent.AuditLogTypeValues))
	})

	It("GET /audit/types returns internal error when service fails", func() {
		fakeSvc.listTypesErr = errors.New("service unavailable")

		req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/api/v1/devops/audit/types", nil)
		Expect(err).To(BeNil())

		router.ServeHTTP(recorder, req)
		Expect(recorder.Code).To(Equal(http.StatusInternalServerError))

		resp := &devopsapi.BaseResp{}
		err = json.Unmarshal(recorder.Body.Bytes(), resp)
		Expect(err).To(BeNil())
		Expect(resp.ErrCode).To(Equal(devopsapi.ErrCodeInternal))
		Expect(resp.ErrMessage).To(ContainSubstring("service unavailable"))
	})
})

type fakeAuditService struct {
	types        []auditent.AuditLogType
	listTypesErr error
}

var _ auditsvc.AuditServiceAPI = (*fakeAuditService)(nil)

func (f *fakeAuditService) ListAuditLog(context.Context, auditsvc.ListAuditLogParam) ([]*auditent.AuditLog, int64, error) {
	return nil, 0, nil
}

func (f *fakeAuditService) ListAuditLogTypes(context.Context) ([]auditent.AuditLogType, error) {
	if f.listTypesErr != nil {
		return nil, f.listTypesErr
	}
	return append([]auditent.AuditLogType(nil), f.types...), nil
}

func (f *fakeAuditService) Create(context.Context, *auditent.AuditLog) error {
	return nil
}
