package tmobile

import (
	"context"
	"math/rand"
	"strconv"
	"sync"
	"time"

	"github.com/fiskerinc/cloud-services/pkg/grpc/sms"
)

// Want to make a client simulator that has a delay for each message, along with a chance of failure
// For use with just await sms testing
type tmobileSimulator struct {
	messages        map[string]fakeMessage
	sy              sync.Mutex
	count           int
	FailPercentage  int           // 10 = 10% chance of failure
	AVGDeliveryTime time.Duration // lets do two seconds
	Deviation       float64       // The number of seconds we can deviate by
	Rando           *rand.Rand    // Using a set seed so we can have consistent results with our random messages
	filter          ICCIDFilter
}

// ChangeDeviceStatus implements TMobClienter.
func (ts *tmobileSimulator) ChangeDeviceStatus(ctx context.Context, cda ChangeDeviceActivation) (err error) {
	panic("unimplemented")
}

type fakeMessage struct {
	finishTime time.Time
	//status     string // Set the time that the message will resolve, and then it will return this resolution. If blank, the message is not going to be delivered within 5 seconds
}

// avgDeliveryTime is time in seconds
func newTMboileSimulator() (tms tmobileSimulator) {
	tms.messages = make(map[string]fakeMessage)
	tms.Rando = rand.New(rand.NewSource(99))
	tms.FailPercentage = 5                // 5% chance of a message not being delivered, so 10,000 will have 500 failed
	tms.Deviation = 2                     // How many seconds, maybe don't use this
	tms.AVGDeliveryTime = time.Second * 2 // On average take 2ish seconds to deliver the message
	return
}

// AccessToken implements TMobClienter.
func (*tmobileSimulator) AccessToken(ctx context.Context) (out *AccessTokenResponse, err error) {
	out = &AccessTokenResponse{}
	out.ExpiresIn = 500000
	return
}

// ChangeRatePlan implements TMobClienter.
func (*tmobileSimulator) ChangeRatePlan(context.Context, *ChangeRatePlanRequest) (*ChangeRatePlanResponse, error) {
	panic("unimplemented")
}

// CustomAttributes implements TMobClienter.
func (*tmobileSimulator) CustomAttributes(context.Context, *CustomAtributesRequest) (*CustomAtributesResponse, error) {
	panic("unimplemented")
}

// Details implements TMobClienter.
func (ts *tmobileSimulator) Details(ctx context.Context, ID string) (out *SMSDetailsResponse, err error) {
	out = &SMSDetailsResponse{}
	msg := ts.messages[ID]
	if msg.finishTime.IsZero() {
		out.Status = "Pending"
		return
	}
	//-cpuprofile cpu.out
	if time.Now().After(msg.finishTime) {
		out.Status = "Delivered"
	} else {
		out.Status = "Pending"
	}

	time.Sleep(time.Millisecond * 40)
	return
}

// DeviceDetails implements TMobClienter.
func (*tmobileSimulator) DeviceDetails(context.Context, *DeviceDetailsRequest) (*DeviceDetailsResponse, error) {
	panic("unimplemented")
}

// GetProducts implements TMobClienter.
func (*tmobileSimulator) GetProducts(context.Context, *sms.GetAvailableProductsRequest) (*sms.GetAvailableProductsResponse, error) {
	panic("unimplemented")
}

// SendSMS implements TMobClienter.
// Call this synchronously as we are using a non-locking map and non-locked index count
func (ts *tmobileSimulator) SendSMS(ctx context.Context, in *SendSMSRequest) (out *SendSMSResponse, err error) {
	ts.sy.Lock()
	defer ts.sy.Unlock()
	out = &SendSMSResponse{}
	ts.messages[strconv.Itoa(ts.count)] = ts.randomDelivery()
	out.SmsMessageID = strconv.Itoa(ts.count)
	ts.count += 1
	return
}

func (ts *tmobileSimulator) randomDelivery() (msg fakeMessage) {
	if ts.Rando.Intn(100) <= ts.FailPercentage {
		return
	}

	// 0 -> .99 of deviation
	// 0:Deviation Value - 1/2 deviation = -.5 deviation -> .5 deviation
	// I think its okay that the time is only going to take longer, no real need for shorter messages
	ranomometer := (ts.Rando.Float64() * ts.Deviation)
	// Actually don't need the status for this test. We have the pending unless its delivered. WE don't really see the failed to delivery message
	msg.finishTime = time.Now().Add(ts.AVGDeliveryTime + time.Duration(ranomometer))
	return
}

// SetAccessToken implements TMobClienter.
func (*tmobileSimulator) SetAccessToken(accessToken string) {
}

// SetFilter implements TMobClienter.
func (ts *tmobileSimulator) SetFilter(filter []string) {
	ts.filter = ICCIDFilter{
		filter: filter,
	}
}

var _ TMobClienter = new(tmobileSimulator)

// with the channel instead of default
//BenchmarkSMSWrapper-16                 1        5009644871 ns/op         3096536 B/op      45650 allocs/op
// flat  flat%   sum%        cum   cum%
// 50ms 31.25% 31.25%       50ms 31.25%  runtime.kevent
// 40ms 25.00% 56.25%       40ms 25.00%  runtime.pthread_cond_wait
// 20ms 12.50% 68.75%       20ms 12.50%  runtime.pthread_cond_signal
// 10ms  6.25% 75.00%       10ms  6.25%  runtime.(*mspan).init
// 10ms  6.25% 81.25%       20ms 12.50%  runtime.mallocgc
// 10ms  6.25% 87.50%       10ms  6.25%  runtime.read
// 10ms  6.25% 93.75%       10ms  6.25%  runtime.usleep
// 10ms  6.25%   100%       10ms  6.25%  runtime.write1
//    0     0%   100%       20ms 12.50%  github.com/fiskerinc/cloud-services/pkg/tmobile.(*SMSClient).SendSMS
//    0     0%   100%       10ms  6.25%  github.com/fiskerinc/cloud-services/pkg/tmobile.(*tmobileSimulator).SendSMS
// with default
//BenchmarkSMSWrapper-16                 1        5110808221 ns/op         2923960 B/op      43176 allocs/op no major difference
// flat  flat%   sum%        cum   cum%
// 50ms 45.45% 45.45%       50ms 45.45%  runtime.pthread_cond_wait
// 20ms 18.18% 63.64%       20ms 18.18%  runtime.pthread_cond_signal
// 10ms  9.09% 72.73%       10ms  9.09%  runtime.arenaIndex (inline)
// 10ms  9.09% 81.82%       10ms  9.09%  runtime.kevent
// 10ms  9.09% 90.91%       10ms  9.09%  runtime.stackpoolalloc
// 10ms  9.09%   100%       10ms  9.09%  runtime.usleep
//    0     0%   100%       10ms  9.09%  github.com/fiskerinc/cloud-services/pkg/tmobile.BenchmarkSMSWrapper
//    0     0%   100%       10ms  9.09%  runtime.copystack
//    0     0%   100%       10ms  9.09%  runtime.findObject
//    0     0%   100%       60ms 54.55%  runtime.findRunnable
