package account

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	accountpb "github.com/tidbcloud/account/idl/pbgen/proto/account"
	"github.com/tidbcloud/aisupport/services/jsm-syncer/config"
	"google.golang.org/grpc"
	"google.golang.org/protobuf/types/known/timestamppb"
)

func TestListOrgUsersUsesOrgScopeFilter(t *testing.T) {
	t.Parallel()

	provider := &fakeProvider{
		usersResp: &accountpb.ListUsersByFilterResponse{
			Users: []*accountpb.User{
				{
					Id:        100,
					Email:     "User@Example.com",
					FirstName: "Ada",
					LastName:  "Lovelace",
				},
				{
					Id:    101,
					Email: "automation-system@pingcap.com",
				},
			},
		},
	}
	client := newClientWithProvider(config.AccountConfig{}, nil, provider)

	users, err := client.ListOrgUsers(context.Background(), "123")
	require.NoError(t, err)
	require.Equal(t, []User{{
		ID:        "100",
		Email:     "User@Example.com",
		FirstName: "Ada",
		LastName:  "Lovelace",
	}}, users)

	require.NotNil(t, provider.listUsersReq)
	filter := provider.listUsersReq.GetFilter()
	require.Equal(t, []accountpb.ScopeType{accountpb.ScopeType_SCOPE_TYPE_ORG}, filter.GetScopeTypes())
	require.Equal(t, []uint64{123}, filter.GetScopeIds())
	require.Equal(t, []accountpb.TargetType{accountpb.TargetType_TARGET_TYPE_USER}, filter.GetTargetTypes())
}

func TestGetUserByEmail(t *testing.T) {
	t.Parallel()

	provider := &fakeProvider{
		userByEmailResp: &accountpb.GetUserByEmailResponse{User: &accountpb.User{
			Id:        100,
			Email:     "User@Example.com",
			FirstName: "Ada",
			LastName:  "Lovelace",
		}},
	}
	client := newClientWithProvider(config.AccountConfig{}, nil, provider)

	user, err := client.GetUserByEmail(context.Background(), " User@Example.com ")
	require.NoError(t, err)
	require.Equal(t, &User{
		ID:        "100",
		Email:     "User@Example.com",
		FirstName: "Ada",
		LastName:  "Lovelace",
	}, user)
	require.Equal(t, "User@Example.com", provider.userByEmailReq.GetEmail())
}

func TestGetUserByEmailExcludesSystemUser(t *testing.T) {
	t.Parallel()

	provider := &fakeProvider{
		userByEmailResp: &accountpb.GetUserByEmailResponse{User: &accountpb.User{
			Id:    100,
			Email: "Automation-System@PingCAP.com",
		}},
	}
	client := newClientWithProvider(config.AccountConfig{}, nil, provider)

	user, err := client.GetUserByEmail(context.Background(), "Automation-System@PingCAP.com")
	require.NoError(t, err)
	require.Nil(t, user)
}

func TestGetUserByID(t *testing.T) {
	t.Parallel()

	provider := &fakeProvider{
		userByIDResp: &accountpb.GetUserByIDResponse{User: &accountpb.User{
			Id:    100,
			Email: "user@example.com",
		}},
	}
	client := newClientWithProvider(config.AccountConfig{}, nil, provider)

	user, err := client.GetUserByID(context.Background(), "100")
	require.NoError(t, err)
	require.Equal(t, &User{ID: "100", Email: "user@example.com"}, user)
	require.Equal(t, uint64(100), provider.userByIDReq.GetUserId())
}

func TestGetUserByIDExcludesSystemUser(t *testing.T) {
	t.Parallel()

	provider := &fakeProvider{
		userByIDResp: &accountpb.GetUserByIDResponse{User: &accountpb.User{
			Id:    100,
			Email: "automation-system@pingcap.com ",
		}},
	}
	client := newClientWithProvider(config.AccountConfig{}, nil, provider)

	user, err := client.GetUserByID(context.Background(), "100")
	require.NoError(t, err)
	require.Nil(t, user)
}

func TestListPartnerCustomersUsesCustomerIDsStatusAndMSPType(t *testing.T) {
	t.Parallel()

	older := timestamppb.New(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC))
	newer := timestamppb.New(time.Date(2026, 2, 1, 0, 0, 0, 0, time.UTC))
	provider := &fakeProvider{
		partnerResp: &accountpb.ListPartnerCustomersResp{
			PartnerCustomers: []*accountpb.PartnerCustomers{
				{Id: 1, PartnerOrgId: 100, CustomerOrgId: 200, Status: "active", EffectedAt: older},
				{Id: 2, PartnerOrgId: 101, CustomerOrgId: 200, Status: "active", EffectedAt: newer},
			},
		},
	}
	client := newClientWithProvider(config.AccountConfig{}, nil, provider)

	customers, err := client.ListPartnerCustomers(context.Background(), []string{"200"})
	require.NoError(t, err)
	require.NotNil(t, provider.partnerReq)
	require.Equal(t, []uint64{200}, provider.partnerReq.GetPartnerCustomerIds())
	require.Equal(t, []string{"active"}, provider.partnerReq.GetStatus())
	require.Equal(t, accountpb.PartnerType_PARTNER_TYPE_MSP, provider.partnerReq.GetPartnerType())
	require.Equal(t, "101", customers["200"].PartnerOrgID)
}

func TestListOrgsUsesPagerAndStatusFilter(t *testing.T) {
	t.Parallel()

	provider := &fakeProvider{
		orgsResp: &accountpb.ListOrgsAdvanceResponse{
			Orgs:  []*accountpb.Org{{Id: 100, Name: "Acme", Status: "active", TimeZone: "UTC"}},
			Total: 1,
		},
	}
	client := newClientWithProvider(config.AccountConfig{}, nil, provider)

	page, err := client.ListOrgs(context.Background(), ListOrgsOptions{
		Page:     2,
		PageSize: 20,
		Statuses: []string{
			"active",
			"",
			" pending ",
		},
	})
	require.NoError(t, err)
	require.Equal(t, 1, page.Total)
	require.Equal(t, []Org{{ID: "100", Name: "Acme", Status: "active", TimeZone: "UTC"}}, page.Orgs)
	require.NotNil(t, provider.listOrgsReq)
	require.Equal(t, int64(2), provider.listOrgsReq.GetPager().GetPage())
	require.Equal(t, int64(20), provider.listOrgsReq.GetPager().GetPageSize())
	require.Equal(t, []string{"active", "pending"}, provider.listOrgsReq.GetFilter().GetStatus())
}

type fakeProvider struct {
	userByEmailReq *accountpb.GetUserByEmailRequest
	userByIDReq    *accountpb.GetUserByIDRequest
	listOrgsReq    *accountpb.ListOrgsParam
	listUsersReq   *accountpb.ListUsersByFilterRequest
	partnerReq     *accountpb.ListPartnerCustomersReq

	userByEmailResp *accountpb.GetUserByEmailResponse
	userByIDResp    *accountpb.GetUserByIDResponse
	orgsResp        *accountpb.ListOrgsAdvanceResponse
	usersResp       *accountpb.ListUsersByFilterResponse
	partnerResp     *accountpb.ListPartnerCustomersResp
}

func (f *fakeProvider) GetUserByEmail(
	ctx context.Context,
	in *accountpb.GetUserByEmailRequest,
	opts ...grpc.CallOption,
) (*accountpb.GetUserByEmailResponse, error) {
	f.userByEmailReq = in
	return f.userByEmailResp, nil
}

func (f *fakeProvider) GetUserByID(
	ctx context.Context,
	in *accountpb.GetUserByIDRequest,
	opts ...grpc.CallOption,
) (*accountpb.GetUserByIDResponse, error) {
	f.userByIDReq = in
	return f.userByIDResp, nil
}

func (f *fakeProvider) ListOrgsAdvance(
	ctx context.Context,
	in *accountpb.ListOrgsParam,
	opts ...grpc.CallOption,
) (*accountpb.ListOrgsAdvanceResponse, error) {
	f.listOrgsReq = in
	return f.orgsResp, nil
}

func (f *fakeProvider) ListUsersByFilter(
	ctx context.Context,
	in *accountpb.ListUsersByFilterRequest,
	opts ...grpc.CallOption,
) (*accountpb.ListUsersByFilterResponse, error) {
	f.listUsersReq = in
	return f.usersResp, nil
}

func (f *fakeProvider) ListPartnerCustomers(
	ctx context.Context,
	in *accountpb.ListPartnerCustomersReq,
	opts ...grpc.CallOption,
) (*accountpb.ListPartnerCustomersResp, error) {
	f.partnerReq = in
	return f.partnerResp, nil
}
