package eligibilityroute

import (
	"fmt"
	"math/rand"
	"sync"
	"time"

	"bitbucket.org/nemt/nemt-portal-api/application/applicationservice"
	"bitbucket.org/nemt/nemt-portal-api/application/third/eligibility/bcbsi"
	"bitbucket.org/nemt/nemt-portal-api/application/viewmodel"
	"bitbucket.org/nemt/nemt-portal-api/infra/auth"
	"bitbucket.org/nemt/nemt-portal-api/infra/config"
	"bitbucket.org/nemt/nemt-portal-api/server/router/routeutils"
	"github.com/labstack/echo"
)

var (
	instance *controller
	once     sync.Once
)

type controller struct {
	cfg   *config.Config
	svc   *applicationservice.Service
	bcbsi *bcbsi.Service
}

func controllerInstance(cfg *config.Config, svc *applicationservice.Service) *controller {
	once.Do(func() {
		instance = &controller{
			cfg:   cfg,
			svc:   svc,
			bcbsi: bcbsi.New(cfg),
		}
	})
	return instance
}

func (c *controller) rangeIn(low, hi int) string {
	result := low + rand.Intn(hi-low)
	return fmt.Sprintf("%v", result)
}

func (c *controller) generatePassword(n int) string {
	const (
		charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	)

	return c.stringWithCharset(n, charset)
}

func (c *controller) stringWithCharset(length int, charset string) string {
	b := make([]byte, length)
	var seededRand *rand.Rand = rand.New(
		rand.NewSource(time.Now().UnixNano()))
	for i := range b {
		b[i] = charset[seededRand.Intn(len(charset))]
	}
	return string(b)
}

func (c *controller) handleEligibility(ctx echo.Context) error {
	var eligibility viewmodel.Eligibility

	if err := ctx.Bind(&eligibility); err != nil {
		return routeutils.HandleAPIError(ctx, err)
	}

	authUser, err := auth.GetUserDetail(ctx, c.cfg)
	if err != nil {
		return routeutils.HandleAPIError(ctx, err)
	}

	var provider viewmodel.ProviderResp
	provider, err = c.svc.Provider.GetByNPI(eligibility.RawProvider.FivePartKeyGroups[0].ProviderNum, authUser)
	if err != nil {
		return routeutils.HandleAPIError(ctx, err)
	}

	if provider.ProviderUUID == "" {
		org := viewmodel.Organization{
			Type: viewmodel.OrganizationType{
				Key:  "provider",
				Name: "Provider",
			},
			Name:        eligibility.RawProvider.OrgName,
			Description: eligibility.RawProvider.OrgName,
			Main:        false,
			Author:      authUser,
			LastEditor:  authUser,
			Reference:   eligibility.RawProvider,
		}

		org, err = c.svc.Organization.AddOrganization(org, authUser)
		if err != nil {
			return routeutils.HandleAPIError(ctx, err)
		}

		provider, err = c.svc.Provider.GetByOrganization(org.UUID, authUser)
		if err != nil {
			return routeutils.HandleAPIError(ctx, err)
		}
	}

	user, err := c.svc.Users.CheckAndCreateMember(eligibility.User, provider, authUser)
	if err != nil {
		if validationError, ok := err.(*viewmodel.ValidationError); ok {
			if len(validationError.Errors) > 0 {
				return routeutils.ResponseAPICustomValidationError(ctx, validationError.Message, validationError.Errors)
			} else {
				return routeutils.ResponseAPIServiceError(ctx, validationError.Message)
			}
		} else {
			return routeutils.HandleAPIError(ctx, err)
		}
	}

	if authUser.Profiles[0].Key == "VIRPT" {
		return ctx.JSON(204, nil)
	} else {
		return ctx.JSON(200, user)
	}
}
