package salesforce

import (
	"encoding/json"
	"errors"
	"time"

	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/util/retry"
)

const (
	syncLeadsApiPrefix          = "/services/apexrest/SyncLeads"
	updateAccountApiPrefix      = "/services/apexrest/UpdateAccount"
	syncPocApplicationApiPrefix = "/services/apexrest/addpocandresults"
)

var (
	defaultRetry = wait.Backoff{
		Steps:    3,
		Duration: 10 * time.Millisecond,
		Factor:   1.0,
		Jitter:   0.1,
	}
)

type Lead struct {
	Email         string `json:"email"`
	Name          string `json:"name"`
	Country       string `json:"country"`
	Plan          string `json:"plan"`
	TimeZone      string `json:"time_zone"`
	PortalID      string `json:"portal_id"`
	FirstName     string `json:"first_name"`
	LastName      string `json:"last_name"`
	CreatedAt     string `json:"created_at"`
	AcceptService int    `json:"accept_service"`
	AgreementTime string `json:"agreement_time"`
	IPAddress     string `json:"ip_address"`
	PhoneNumber   string `json:"phone_number"`
	Job           string `json:"job"`
	Source        string `json:"source"`
	// The following fields are custom fields of Salesforce by Biz Ops.
	// The field name should be small case.
	// These first-level fields are automatically generated for the Salesforce SyncLeads API.
	// Refer to the Salesforce Dev, they can't have these fields as nested JSON fields.
	ConnectionType     string `json:"Connection_Type"`
	WebsiteReferrerURL string `json:"Website_Referrer_URL"`
	UtmCampaign        string `json:"utm_campaign"`
	UtmContent         string `json:"utm_content"`
	UtmMedium          string `json:"utm_medium"`
	UtmSource          string `json:"utm_source"`
	UtmTerm            string `json:"utm_term"`
}

func (c *Client) syncLeads(lead Lead) error {
	resp, err := c.post(syncLeadsApiPrefix, lead)
	if err != nil {
		return err
	}

	var result Response
	err = json.Unmarshal(resp, &result)
	if err != nil {
		return err
	}
	if result.Code == "0" {
		return nil
	}
	return errors.New(string(resp))
}

// SyncLeads API
func (c *Client) SyncLeads(lead Lead) error {
	return retry.OnError(defaultRetry, func(e error) bool { return e != nil }, func() error {
		return c.syncLeads(lead)
	})
}

type Account struct {
	Plan             string `json:"plan"`
	PortalID         string `json:"portal_id"`
	BillingEmail     string `json:"billing_email"`
	BindCreditCardAt string `json:"bind_credit_card_at"`
}

func (c *Client) updateAccount(account Account) error {
	resp, err := c.post(updateAccountApiPrefix, account)
	if err != nil {
		return err
	}

	var result Response
	err = json.Unmarshal(resp, &result)
	if err != nil {
		return err
	}
	if result.Code == "0" {
		return nil
	}
	return errors.New(string(resp))
}

// UpdateAccount API
func (c *Client) UpdateAccount(account Account) error {
	return retry.OnError(defaultRetry, func(e error) bool { return e != nil }, func() error {
		return c.updateAccount(account)
	})
}

type PocApplication struct {
	PortalID   string        `json:"portalId"`
	PocID      string        `json:"pocId"`
	PocResults []PocQuestion `json:"pocResults"`
}

type PocQuestionType string

const (
	PocQuestionTypeRadio    PocQuestionType = "Radio"
	PocQuestionTypeCheckbox PocQuestionType = "Checkbox"
	PocQuestionTypeText     PocQuestionType = "Text"
)

type PocQuestion struct {
	QuestionNumber int                 `json:"questionNumber"`
	Type           PocQuestionType     `json:"type"`
	Question       string              `json:"question"`
	Options        []PocQuestionOption `json:"options"`
	Note           string              `json:"note"`
}

type PocQuestionOption struct {
	OptionName string `json:"optionName"`
	IsSelected bool   `json:"isSelected"`
}

func (c *Client) syncPocApplication(pocApplication PocApplication) error {
	resp, err := c.post(syncPocApplicationApiPrefix, pocApplication)
	if err != nil {
		return err
	}

	var result Response
	err = json.Unmarshal(resp, &result)
	if err != nil {
		return err
	}
	if result.Code == "0" {
		return nil
	}
	return errors.New(string(resp))
}

func (c *Client) SyncPocApplication(pocApplication PocApplication) error {
	return retry.OnError(defaultRetry, func(e error) bool { return e != nil }, func() error {
		return c.syncPocApplication(pocApplication)
	})
}
