// Package smsglobal allows bulk messaging to a desired recipient list
package smsglobal

import (
	"errors"
	"flag"
	"net/http"
	"net/url"
	"strings"

	"github.com/thrasher-corp/gocryptotrader/common"
	"github.com/thrasher-corp/gocryptotrader/communications/base"
	"github.com/thrasher-corp/gocryptotrader/log"
)

const (
	smsGlobalAPIURL = "https://www.smsglobal.com/http-api.php"
)

var (
	errSMSNotSent      = errors.New("SMSGlobal message not sent")
	errContactNotFound = errors.New("SMSGlobal error contact not found")
)

// SMSGlobal is the overarching type across this package
type SMSGlobal struct {
	base.Base
	Contacts []Contact
	Username string
	Password string
	SendFrom string
}

// Setup takes in a SMSGlobal configuration, sets username, password and
// and recipient list
func (s *SMSGlobal) Setup(cfg *base.CommunicationsConfig) {
	s.Name = cfg.SMSGlobalConfig.Name
	s.Enabled = cfg.SMSGlobalConfig.Enabled
	s.Verbose = cfg.SMSGlobalConfig.Verbose
	s.Username = cfg.SMSGlobalConfig.Username
	s.Password = cfg.SMSGlobalConfig.Password
	s.SendFrom = cfg.SMSGlobalConfig.From

	var contacts []Contact
	for x := range cfg.SMSGlobalConfig.Contacts {
		contacts = append(contacts,
			Contact{
				Name:    cfg.SMSGlobalConfig.Contacts[x].Name,
				Number:  cfg.SMSGlobalConfig.Contacts[x].Number,
				Enabled: cfg.SMSGlobalConfig.Contacts[x].Enabled,
			},
		)
		log.Debugf(log.CommunicationMgr, "SMSGlobal: SMS Contact: %s. Number: %s. Enabled: %v\n",
			cfg.SMSGlobalConfig.Contacts[x].Name,
			cfg.SMSGlobalConfig.Contacts[x].Number,
			cfg.SMSGlobalConfig.Contacts[x].Enabled)
	}
	s.Contacts = contacts
}

// IsConnected returns whether or not the connection is connected
func (s *SMSGlobal) IsConnected() bool {
	return s.Connected
}

// Connect connects to the service
func (s *SMSGlobal) Connect() error {
	s.Connected = true
	return nil
}

// PushEvent pushes an event to a contact list via SMS
func (s *SMSGlobal) PushEvent(event base.Event) error {
	return s.SendMessageToAll(event.Message)
}

// GetEnabledContacts returns how many SMS contacts are enabled in the
// contact list
func (s *SMSGlobal) GetEnabledContacts() int {
	counter := 0
	for x := range s.Contacts {
		if s.Contacts[x].Enabled {
			counter++
		}
	}
	return counter
}

// GetContactByNumber returns a contact with supplied number
func (s *SMSGlobal) GetContactByNumber(number string) (Contact, error) {
	for x := range s.Contacts {
		if s.Contacts[x].Number == number {
			return s.Contacts[x], nil
		}
	}
	return Contact{}, errContactNotFound
}

// GetContactByName returns a contact with supplied name
func (s *SMSGlobal) GetContactByName(name string) (Contact, error) {
	for x := range s.Contacts {
		if strings.EqualFold(s.Contacts[x].Name, name) {
			return s.Contacts[x], nil
		}
	}
	return Contact{}, errContactNotFound
}

// AddContact checks to see if a contact exists and adds them if it doesn't
func (s *SMSGlobal) AddContact(contact Contact) error {
	if contact.Name == "" || contact.Number == "" {
		return errors.New("SMSGlobal AddContact() error - nothing to add")
	}

	if s.ContactExists(contact) {
		return errors.New("SMSGlobal AddContact() error - contact already exists")
	}

	s.Contacts = append(s.Contacts, contact)
	return nil
}

// ContactExists checks to see if a contact exists
func (s *SMSGlobal) ContactExists(contact Contact) bool {
	for x := range s.Contacts {
		if s.Contacts[x].Number == contact.Number && strings.EqualFold(s.Contacts[x].Name, contact.Name) {
			return true
		}
	}
	return false
}

// RemoveContact removes a contact if it exists
func (s *SMSGlobal) RemoveContact(contact Contact) error {
	if !s.ContactExists(contact) {
		return errors.New("SMSGlobal RemoveContact() error - contact does not exist")
	}

	for x := range s.Contacts {
		if s.Contacts[x].Name == contact.Name && s.Contacts[x].Number == contact.Number {
			s.Contacts = append(s.Contacts[:x], s.Contacts[x+1:]...)
			return nil
		}
	}
	return errors.New("SMSGlobal RemoveContact() error - contact already removed")
}

// SendMessageToAll sends a message to all enabled contacts in cfg
func (s *SMSGlobal) SendMessageToAll(message string) error {
	for x := range s.Contacts {
		if s.Contacts[x].Enabled {
			if s.Verbose {
				log.Debugf(log.CommunicationMgr, "SMSGlobal: Sending SMS to %s. Number: %s. Message: %s [From: %s]\n",
					s.Contacts[x].Name, s.Contacts[x].Number, message, s.SendFrom)
			}
			err := s.SendMessage(s.Contacts[x].Number, message)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

// SendMessage sends a message to an individual contact
func (s *SMSGlobal) SendMessage(to, message string) error {
	if flag.Lookup("test.v") != nil {
		return nil
	}

	values := url.Values{}
	values.Set("action", "sendsms")
	values.Set("user", s.Username)
	values.Set("password", s.Password)
	values.Set("from", s.SendFrom)
	values.Set("to", to)
	values.Set("text", message)

	headers := make(map[string]string)
	headers["Content-Type"] = "application/x-www-form-urlencoded"

	resp, err := common.SendHTTPRequest(http.MethodPost,
		smsGlobalAPIURL,
		headers,
		strings.NewReader(values.Encode()))
	if err != nil {
		return err
	}

	if !strings.Contains(resp, "OK: 0; Sent queued message") {
		return errSMSNotSent
	}
	return nil
}
