package autocredit

import (
	"context"
	"sync"
	"testing"

	"github.com/go-logr/logr"
	"go.uber.org/mock/gomock"

	tntent "github.com/tidbcloud/devops/entity/tenant"
	"github.com/tidbcloud/devops/pkg/infra/clients/billingcli"
	"github.com/tidbcloud/devops/pkg/infra/clients/googlecli"
	"github.com/tidbcloud/devops/pkg/infra/config"
	"github.com/tidbcloud/devops/pkg/service"
	tenantsvc "github.com/tidbcloud/devops/pkg/service/tenant"
)

type fakeSheetsCli struct {
	rows [][]any
	err  error
}

func (f *fakeSheetsCli) ReadRange(ctx context.Context, spreadsheetID string, readRange string) ([][]any, error) {
	return f.rows, f.err
}

type fakeGoogleClient struct {
	sheets googlecli.SheetsCli
}

func (f *fakeGoogleClient) Admin() googlecli.AdminCli {
	return nil
}

func (f *fakeGoogleClient) BigQuery() googlecli.BigQueryCli {
	return nil
}

func (f *fakeGoogleClient) Sheets() googlecli.SheetsCli {
	return f.sheets
}

func TestNormalizeEmails(t *testing.T) {
	rows := [][]any{
		{"Email"},
		{" Foo@Example.com "},
		{""},
		{"foo@example.com"},
		{"bar@example.com"},
	}

	got := normalizeEmails(rows)
	want := []string{"foo@example.com", "bar@example.com"}
	if len(got) != len(want) {
		t.Fatalf("unexpected email count: got=%v want=%v", got, want)
	}
	for i := range want {
		if got[i] != want[i] {
			t.Fatalf("unexpected emails: got=%v want=%v", got, want)
		}
	}
}

func TestRunOnceCreatesCredit(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	tenantMock := tenantsvc.NewMockTenantServiceAPI(ctrl)
	billingMock := billingcli.NewMockClient(ctrl)

	conf := config.AutoCreditServiceConfig{
		SheetID:         "sheet-id",
		SheetRange:      "Sheet1!A:A",
		CreditAmount:    "100",
		Usage:           "startup_program",
		Provider:        "aws",
		DiscountEnabled: true,
		NotePrefix:      "AUTO_CREDIT_TEST",
		OperatorEmail:   "fan.wang@pingcap.com",
	}

	s := &svc{
		JobService: service.NewJobService("AutoCreditService", service.WithDummy(false)),
		conf:       conf,
		googleCli:  &fakeGoogleClient{sheets: &fakeSheetsCli{rows: [][]any{{"tenant@example.com"}}}},
		billingCli: billingMock,
		tenantSvc:  tenantMock,
		mutex:      &sync.Mutex{},
	}
	_ = logr.Discard()

	tenantMock.EXPECT().ListTenants(gomock.Any(), gomock.AssignableToTypeOf(tenantsvc.ListTenantsParam{})).DoAndReturn(
		func(ctx context.Context, param tenantsvc.ListTenantsParam) ([]*tntent.Tenant, int64, error) {
			if param.Search != "tenant@example.com" {
				t.Fatalf("unexpected tenant search: %s", param.Search)
			}
			return []*tntent.Tenant{{Id: 1001, Email: "tenant@example.com"}}, 1, nil
		},
	)
	billingMock.EXPECT().ListBillingConfigs(gomock.Any(), uint64(1001), "fan.wang@pingcap.com").Return([]*billingcli.BillingConfig{
		{BillingConfigID: 2002, Status: billingConfigStatusActive},
	}, nil)
	billingMock.EXPECT().ListCredits(gomock.Any(), uint64(1001), uint64(2002), "fan.wang@pingcap.com").Return(nil, nil)
	billingMock.EXPECT().CreateCreditsWithBillingConfig(gomock.Any(), uint64(1001), uint64(2002), gomock.Any(), "fan.wang@pingcap.com").DoAndReturn(
		func(ctx context.Context, tenantID uint64, billingConfigID uint64, req *billingcli.CreateCreditsWithBillingConfigReq, operatorEmail string) error {
			if req.Note != "AUTO_CREDIT_TEST:tenant@example.com" {
				t.Fatalf("unexpected note: %s", req.Note)
			}
			if req.TotalCredits != "100" {
				t.Fatalf("unexpected credit amount: %s", req.TotalCredits)
			}
			if req.Usage != "startup_program" {
				t.Fatalf("unexpected usage: %s", req.Usage)
			}
			return nil
		},
	)

	if err := s.RunOnce(context.Background()); err != nil {
		t.Fatalf("RunOnce returned error: %v", err)
	}
}
