package app

import (
	"fmt"

	"code.tnxs.net/taxnexus/lib/api/crm/crm_client/accounts"
	"code.tnxs.net/taxnexus/lib/api/geo/geo_client/coordinate"
	"code.tnxs.net/taxnexus/lib/api/geo/geo_models"
)

// GetDefaultDeliveryAddress is an account helper function
func GetDefaultDeliveryAddress(obj *Account, principal *User) (*Address, error) {
	var deliveryAddress Address
	theContact, err := GetContactByID(obj.DefaultDeliveryContactID, principal)
	if err != nil {
		sugar.Errorf("ops.getDefaultAddress: 💣 ⛔ theAccount.Defaultdeliverycontactid invalid %w", err)
		if obj.ShippingAddress == nil {
			if obj.BillingAddress == nil {
				return nil, fmt.Errorf("ops.GetDefaultDeliveryAddress: 💣 ⛔ can't find any valid addresses")
			}
			deliveryAddress = *obj.BillingAddress
		} else {
			deliveryAddress = *obj.ShippingAddress
		}
	} else {
		if theContact.MailingAddress == nil {
			sugar.Errorf(
				"ops.getDefaultDeliveryAddress: 💣 ⛔ default delivery contact has no email, id = %s",
				obj.DefaultBackendID,
			)
			if obj.ShippingAddress == nil {
				if obj.BillingAddress == nil {
					return nil, fmt.Errorf("ops.GetDefaultDeliveryAddress: 💣 ⛔ can't find any valid addresses")
				}
				deliveryAddress = *obj.BillingAddress
			} else {
				deliveryAddress = *obj.ShippingAddress
			}
		} else {
			deliveryAddress = *theContact.MailingAddress
		}
	}
	if deliveryAddress.City == "" || deliveryAddress.State+deliveryAddress.StateCode == "" {
		return nil, fmt.Errorf("ops.getDefaultDeliveryAddress: 💣 ⛔ city or state+statecode is blank: %v", deliveryAddress)
	}
	return &deliveryAddress, nil
}

// GetAccount is first class retrieval function
func GetAccount(key string, principal *User) *Account {
	if key == "" {
		return nil
	}
	a, ok := accountCache.get(key)
	if ok {
		return a
	}
	acct, err := GetAccountByID(key, principal)
	if err != nil {
		return nil
	}
	return acct
}

// GetAccountByID retrieves and enriches an Account object instance
func GetAccountByID(recordID string, principal *User) (*Account, error) {
	sugar.Debug("GetAccountByID: 📥")
	if recordID == "" {
		return nil, fmt.Errorf("getAccountByID: 💣 ⛔ key is blank")
	}
	obj, ok := accountCache.get(recordID)
	if ok {
		sugar.Debug("getAccountByID: 👍 🎯 📤")
		return obj, nil
	}
	crmParams := accounts.NewGetAccountsParamsWithTimeout(getTimeout)
	crmParams.AccountID = &recordID
	response, err := crmClient.Accounts.GetAccounts(crmParams, principal.Auth)
	if err != nil {
		return nil, err
	}
	var theObj *Account
	for _, itm := range response.Payload.Data { // single iteration execution
		theObj = UnMarshalAccount(itm)
	}
	finalObj := theObj.Enrich(principal)
	accountCache.put(recordID, finalObj)
	sugar.Debug("getAccountByID: 👍 🆕 📤")
	return finalObj, nil
}

// Enrich adds subordinate objects to a first class object and returns a copy
func (obj *Account) Enrich(principal *User) *Account {
	return &Account{
		ID:                       obj.ID,
		AccountNumber:            obj.AccountNumber,
		AccountSource:            obj.AccountSource,
		Active:                   obj.Active,
		AdministrativeLevel:      obj.AdministrativeLevel,
		Amount:                   obj.Amount,
		AmountInvoiced:           obj.AmountInvoiced,
		AmountPaid:               obj.AmountPaid,
		AnnualRevenue:            obj.AnnualRevenue,
		Balance:                  obj.Balance,
		BillingAddress:           obj.BillingAddress,
		BillingContactID:         obj.BillingContactID,
		BillingContact:           GetContact(obj.BillingContactID, principal),
		BillingPreference:        obj.BillingPreference,
		BusinessAddress:          obj.BusinessAddress,
		CannabisCustomer:         obj.CannabisCustomer,
		ChannelProgramLevelName:  obj.ChannelProgramLevelName,
		ChannelProgramName:       obj.ChannelProgramName,
		ClientEndDate:            obj.ClientEndDate,
		ClientStartDate:          obj.ClientStartDate,
		CompanyID:                obj.CompanyID,
		Coordinate:               obj.GetCoordinate(principal),
		CoordinateID:             obj.CoordinateID,
		CreatedByID:              obj.CreatedByID,
		CreatedDate:              obj.CreatedDate,
		CustomerID:               obj.CustomerID,
		CustomerPriority:         obj.CustomerPriority,
		DandBCompanyID:           obj.DandBCompanyID,
		DBA:                      obj.DBA,
		DefaultAddress:           obj.DefaultAddress,
		DefaultBackendID:         obj.DefaultBackendID,
		DefaultBackend:           GetBackend(obj.DefaultBackendID, principal),
		DefaultDeliveryContactID: obj.DefaultDeliveryContactID,
		DefaultEndUserID:         obj.DefaultEndUserID,
		DefaultEndUser:           GetContact(obj.DefaultEndUserID, principal),
		Description:              obj.Description,
		DunsNumber:               obj.DunsNumber,
		EIN:                      obj.EIN,
		Email:                    obj.Email,
		EnrollmentStatus:         obj.EnrollmentStatus,
		Fax:                      obj.Fax,
		Industry:                 obj.Industry,
		IsCustomerPortal:         obj.IsCustomerPortal,
		IsPartner:                obj.IsPartner,
		ISPCustomer:              obj.ISPCustomer,
		Jigsaw:                   obj.Jigsaw,
		LastModifiedByID:         obj.LastModifiedByID,
		LastModifiedDate:         obj.LastModifiedDate,
		MSPCustomer:              obj.MSPCustomer,
		NaicsCode:                obj.NaicsCode,
		NaicsDesc:                obj.NaicsDesc,
		Name:                     obj.Name,
		NumberOfEmployees:        obj.NumberOfEmployees,
		NumberOfLocations:        obj.NumberOfLocations,
		OpenCharges:              obj.OpenCharges,
		OrderContactID:           obj.OrderContactID,
		OrderContact:             GetContact(obj.OrderContactID, principal),
		OrderEmail:               obj.OrderEmail,
		OwnerID:                  obj.OwnerID,
		Ownership:                obj.Ownership,
		ParentFK:                 obj.ParentFK,
		ParentID:                 obj.ParentID,
		Phone:                    obj.Phone,
		PlaceID:                  obj.PlaceID,
		PreparerID:               obj.PreparerID,
		Rating:                   obj.Rating,
		RatingEngineID:           obj.RatingEngineID,
		Ref:                      obj.Ref,
		RevenueBase:              obj.RevenueBase,
		RevenueNet:               obj.RevenueNet,
		RevenueNotTaxable:        obj.RevenueNotTaxable,
		ShippingAddress:          obj.ShippingAddress,
		ShippingCensusTract:      obj.ShippingCensusTract,
		ShippingContactID:        obj.ShippingContactID,
		ShippingContact:          GetContact(obj.ShippingContactID, principal),
		ShippingCounty:           obj.ShippingCounty,
		SIC:                      obj.SIC,
		SicDesc:                  obj.SicDesc,
		Site:                     obj.Site,
		Status:                   obj.Status,
		TaxExemption:             obj.TaxExemption,
		TaxOnTax:                 obj.TaxOnTax,
		TelecomCustomer:          obj.TelecomCustomer,
		TenantID:                 obj.TenantID,
		TickerSymbol:             obj.TickerSymbol,
		TradeStyle:               obj.TradeStyle,
		Type:                     obj.Type,
		UnappliedPayments:        obj.UnappliedPayments,
		UnitBase:                 obj.UnitBase,
		UpsellOpportunity:        obj.UpsellOpportunity,
		Website:                  obj.Website,
		WHMCSClientID:            obj.WHMCSClientID,
		XeroContactID:            obj.XeroContactID,
		YearStarted:              obj.YearStarted,
	}
}

// GetCoordinate is a first class retrieval function
func (obj *Account) GetCoordinate(principal *User) *Coordinate {
	sugar.Debug("Account.getCoordinate: 📥")
	if obj.CoordinateID != "" { // if CoordinateID is set, then just get it
		geoParams := coordinate.NewGetCoordinatesParamsWithTimeout(getTimeout)
		geoParams.CoordinateID = &obj.CoordinateID
		response, err := geoClient.Coordinate.GetCoordinates(geoParams, principal.Auth)
		if err != nil {
			return nil
		}
		var obj *Coordinate
		for _, itm := range response.Payload.Data { // single iteration execution
			obj = UnMarshalCoordinate(itm, principal)
		}
		return obj
	} // else get it via addresses, shipping #1, Business #2
	if obj.ShippingAddress.ToString() == "" && obj.BillingAddress.ToString() == "" {
		sugar.Errorf("Account.getCoordinate: 💣 ⛔ billing and shipping address both blank")
		return nil
	}
	theAddress := obj.ShippingAddress.ToString()
	if theAddress == "" {
		theAddress = obj.BillingAddress.ToString()
	}
	geoParams := coordinate.NewGetCoordinatesParamsWithTimeout(getTimeout)
	geoParams.Address = &theAddress
	response, err := geoClient.Coordinate.GetCoordinates(geoParams, principal.Auth)
	if err != nil {
		sugar.Error(err)
		return nil
	}
	var swag *geo_models.Coordinate
	for _, itm := range response.Payload.Data { // single iteration execution
		swag = itm
	}
	sugar.Debug("Account.getCoordinate: 👍 📤")
	return UnMarshalCoordinate(swag, principal)
}
