package v1

import (
	"context"
	"fmt"
	"os"

	"github.com/pingcap-inc/pchub/pkg/log"
	"github.com/spf13/viper"
	"golang.org/x/oauth2/google"
	admin "google.golang.org/api/admin/directory/v1"
	report "google.golang.org/api/admin/reports/v1"
	alertcenter "google.golang.org/api/alertcenter/v1beta1"
	"google.golang.org/api/gmail/v1"
	"google.golang.org/api/option"
)

type googleService struct {
}

type GoogleServiceInterface interface {
	WsSrv(ctx context.Context, wsType string, scope ...string) (interface{}, error)
}

func NewGoogleService() GoogleServiceInterface {
	return &googleService{}
}

func (g *googleService) WsSrv(ctx context.Context, wsType string, scope ...string) (interface{}, error) {
	if len(scope) == 0 {
		return nil, fmt.Errorf("google: google client scope is required")
	}

	gcsServiceAccountFile := viper.GetString("ws-sa")
	_, err := os.Stat(gcsServiceAccountFile)
	if err != nil {
		log.L(ctx).Errorf("Failed to get file info: %v", err)

		return nil, err
	}

	serviceAccount, err := os.ReadFile(gcsServiceAccountFile)
	if err != nil {
		log.L(ctx).Errorf("Failed to read service account file: %v", err)
	}

	var adSrv interface{}
	switch wsType {
	case "admin":
		adSrv, err = g.newAdminSrv(ctx, dwdGCPSubject, serviceAccount, scope...)
	case "gmail":
		adSrv, err = g.newGmailSrv(ctx, dwdGCPSubject, serviceAccount, scope...)
	case "alertcenter":
		adSrv, err = g.newAlertCenterSrv(ctx, dwdGCPSubject, serviceAccount, scope...)
	case "report":
		adSrv, err = g.newReportSrv(ctx, dwdGCPSubject, serviceAccount, scope...)
	case "ak":
		adSrv, err = g.ak(ctx, dwdGCPSubject, serviceAccount, scope...)

	default:
		return nil, fmt.Errorf("google: google client scope is required")
	}
	if err != nil {
		log.L(ctx).Errorf("admin.NewService error: %v", err)

		return nil, err
	}

	return adSrv, nil
}

func (g *googleService) newAdminSrv(ctx context.Context, userEmail string, serviceAccount []byte, scope ...string) (*admin.Service, error) {
	if len(scope) == 0 {
		return nil, fmt.Errorf("google: google client scope is required")
	}

	config, err := google.JWTConfigFromJSON(serviceAccount, scope...)
	if err != nil {
		return nil, fmt.Errorf("google: error getting JWT config from Service Account: %v", err)
	}

	config.Subject = userEmail
	ts := config.TokenSource(ctx)
	svc, err := admin.NewService(ctx, option.WithTokenSource(ts))
	// svc, err := admin.NewGroupsSettingService(ctx, options.WithTokenSource(ts))
	if err != nil {
		return nil, fmt.Errorf("google: error creating service: %v", err)
	}

	return svc, nil
}

func (g *googleService) ak(ctx context.Context, userEmail string, serviceAccount []byte, scope ...string) (string, error) {
	if len(scope) == 0 {
		return "", fmt.Errorf("google: google client scope is required")
	}

	config, err := google.JWTConfigFromJSON(serviceAccount, scope...)
	if err != nil {
		return "", fmt.Errorf("google: error getting JWT config from Service Account: %v", err)
	}

	config.Subject = userEmail
	ts := config.TokenSource(ctx)

	tk, err := ts.Token()
	if err != nil {
		return "", fmt.Errorf("google: error getting token: %v", err)
	}

	return tk.AccessToken, nil
}

func (g *googleService) newGmailSrv(ctx context.Context, userEmail string, serviceAccount []byte, scope ...string) (*gmail.Service, error) {
	if len(scope) == 0 {
		return nil, fmt.Errorf("google: google client scope is required")
	}

	config, err := google.JWTConfigFromJSON(serviceAccount, scope...)
	if err != nil {
		return nil, fmt.Errorf("google: error getting JWT config from Service Account: %v", err)
	}

	config.Subject = userEmail
	ts := config.TokenSource(ctx)
	svc, err := gmail.NewService(ctx, option.WithTokenSource(ts))
	// svc, err := admin.NewGroupsSettingService(ctx, options.WithTokenSource(ts))
	if err != nil {
		return nil, fmt.Errorf("google: error creating service: %v", err)
	}

	return svc, nil
}

func (g *googleService) newAlertCenterSrv(ctx context.Context, userEmail string, serviceAccount []byte, scope ...string) (*alertcenter.Service, error) {
	if len(scope) == 0 {
		return nil, fmt.Errorf("google: google client scope is required")
	}

	config, err := google.JWTConfigFromJSON(serviceAccount, scope...)
	if err != nil {
		return nil, fmt.Errorf("google: error getting JWT config from Service Account: %v", err)
	}

	config.Subject = userEmail
	ts := config.TokenSource(ctx)
	svc, err := alertcenter.NewService(ctx, option.WithTokenSource(ts))
	// svc, err := admin.NewGroupsSettingService(ctx, options.WithTokenSource(ts))
	if err != nil {
		return nil, fmt.Errorf("google: error creating service: %v", err)
	}

	return svc, nil
}

func (g *googleService) newReportSrv(ctx context.Context, userEmail string, serviceAccount []byte, scope ...string) (*report.Service, error) {
	if len(scope) == 0 {
		return nil, fmt.Errorf("google: google client scope is required")
	}

	config, err := google.JWTConfigFromJSON(serviceAccount, scope...)
	if err != nil {
		return nil, fmt.Errorf("google: error getting JWT config from Service Account: %v", err)
	}

	config.Subject = userEmail
	ts := config.TokenSource(ctx)
	svc, err := report.NewService(ctx, option.WithTokenSource(ts))
	// svc, err := admin.NewGroupsSettingService(ctx, options.WithTokenSource(ts))
	if err != nil {
		return nil, fmt.Errorf("google: error creating service: %v", err)
	}

	return svc, nil
}
