package dao

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/smartystreets/goconvey/convey"
	"github.com/tidbcloud/tiinf/sdk/auditlog"
	"gorm.io/gorm"

	"github.com/tidbcloud/auditlog/configs"
	"github.com/tidbcloud/auditlog/internal/entity"
	"github.com/tidbcloud/auditlog/types"
)

var (
	db *gorm.DB
)

func init() {
	cfg := configs.GetConfig()
	entity.InitDB(cfg, true) //nolint:errcheck
	db = entity.GetDB()
}

func deleteEventByID(id uint) {
	db.Delete(&entity.Event{}, "id = ?", id)
}

func TestAuditDao_CreateEvent(t *testing.T) {
	d := NewAuditDao()
	ctx := context.TODO()

	convey.Convey("CreateEvent", t, func() {
		e := entity.Event{
			Type:                auditlog.EventChangePassword,
			EndsAt:              time.Now(),
			OperatorType:        auditlog.OperatorTypeUser,
			OperatorID:          1,
			OperatorIP:          "1.2.3.4",
			OperatorLoginMethod: "google",
			OrgID:               2,
			ProjectID:           3,
			ClusterID:           4,
			TraceID:             "",
			Result:              auditlog.ResultSuccess,
			Details:             "{}",
		}
		err := d.CreateEvent(ctx, db, &e)
		convey.So(err, convey.ShouldBeNil)
		deleteEventByID(e.ID)
	})
}

func TestAuditDao_DeleteEventsOlderThan(t *testing.T) {
	d := NewAuditDao()
	ctx := context.TODO()

	convey.Convey("DeleteEventsOlderThan", t, func() {
		now := time.Now()
		yesterday1 := now.AddDate(0, 0, -1)
		yesterday2 := now.AddDate(0, 0, -1).Add(time.Minute)

		fn := func(t time.Time) {
			err := d.CreateEvent(ctx, db, &entity.Event{
				Type:                auditlog.EventImportDataFromGCP,
				EndsAt:              time.Now(),
				OperatorType:        auditlog.OperatorTypeUser,
				OperatorID:          1,
				OperatorIP:          "",
				OperatorLoginMethod: "google",
				OrgID:               2,
				ProjectID:           3,
				ClusterID:           4,
				TraceID:             "",
				Result:              auditlog.ResultSuccess,
				Details:             "{}",
			})
			convey.So(err, convey.ShouldBeNil)
		}

		fn(now)
		fn(yesterday1)
		fn(yesterday2)

		err := d.DeleteEventsOlderThan(ctx, db, yesterday2)
		convey.So(err, convey.ShouldBeNil)

		var count int64
		db.Model(&entity.Event{}).Count(&count)
		convey.So(count, convey.ShouldBeGreaterThanOrEqualTo, 2)
	})
}

func TestAuditDao_QueryEvents(t *testing.T) {
	convey.Convey("QueryEvents", t, func() {
		email := "someone@pingcap.com"
		now := time.Now()
		event := entity.Event{
			Type:                auditlog.EventImportDataFromGCP,
			EndsAt:              now,
			OperatorType:        auditlog.OperatorTypeUser,
			OperatorID:          1,
			OperatorEmail:       email,
			OperatorIP:          "1.2.3.4",
			OperatorLoginMethod: "google",
			OrgID:               3,
			OrgName:             "org_name",
			ProjectID:           4,
			ProjectName:         "project_name",
			ClusterID:           5,
			ClusterName:         "cluster_name",
			TraceID:             "trace_id",
			Result:              1,
			Details:             "details",
		}
		err := db.Create(&event).Error
		convey.So(err, convey.ShouldBeNil)
		defer deleteEventByID(event.ID)

		startAt := now.Add(time.Hour * time.Duration(-1))
		endAt := now.Add(time.Hour * time.Duration(1))
		filter := types.QueryAuditLogFilter{
			EventTypes:          fmt.Sprintf("%d,%d", auditlog.EventImportDataFromGCP, auditlog.EventImportDataFromAWS),
			Result:              1,
			StartedAt:           startAt.Unix(),
			EndedAt:             endAt.Unix(),
			OperatorType:        auditlog.OperatorTypeUser,
			OperatorID:          1,
			OperatorEmail:       email,
			OperatorIP:          "1.2.3.4",
			OperatorLoginMethod: "google",
			TraceID:             "trace_id",
			OrgID:               3,
			OrgName:             "org_name",
			ProjectID:           4,
			ProjectName:         "project_name",
			ClusterID:           5,
			ClusterName:         "cluster_name",
		}

		d := NewAuditDao()
		ctx := context.TODO()

		items, _, err := d.QueryEvents(ctx, db, filter, false)
		convey.So(err, convey.ShouldBeNil)
		convey.So(len(items), convey.ShouldEqual, 1)
	})
}
