package gdpr

import (
	"github.com/prebid/go-gdpr/api"
	"github.com/prebid/go-gdpr/consentconstants"
	tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2"
	"github.com/prebid/prebid-server/v4/config"
	"github.com/prebid/prebid-server/v4/openrtb_ext"
)

// PurposeEnforcer represents the enforcement strategy for determining if legal basis is achieved for a purpose
type PurposeEnforcer interface {
	LegalBasis(vendorInfo VendorInfo, name string, consent tcf2.ConsentMetadata, overrides Overrides) bool
}

// PurposeEnforcerBuilder generates an instance of PurposeEnforcer for a given purpose and bidder
type PurposeEnforcerBuilder func(p consentconstants.Purpose, name string) PurposeEnforcer

// Overrides specifies enforcement algorithm rule adjustments
type Overrides struct {
	allowLITransparency   bool
	blockVendorExceptions bool
	enforcePurpose        bool
	enforceVendors        bool
}

type BidderInfo struct {
	bidderCoreName openrtb_ext.BidderName
	bidder         openrtb_ext.BidderName
}
type VendorInfo struct {
	vendorID uint16
	vendor   api.Vendor
}

// PurposeEnforcers holds the full and basic enforcers for a purpose
type PurposeEnforcers struct {
	Full  PurposeEnforcer
	Basic PurposeEnforcer
}

// NewPurposeEnforcerBuilder creates a new instance of PurposeEnforcerBuilder. This function uses
// closures so that any enforcer generated by the returned builder may use the config and also be
// cached and reused within a request context
func NewPurposeEnforcerBuilder(cfg TCF2ConfigReader) PurposeEnforcerBuilder {
	cachedEnforcers := make([]PurposeEnforcers, 10)

	return func(purpose consentconstants.Purpose, name string) PurposeEnforcer {
		index := purpose - 1

		var basicEnforcementVendor bool
		if purpose == consentconstants.Purpose(1) {
			basicEnforcementVendor = false
		} else {
			basicEnforcementVendors := cfg.BasicEnforcementVendors()
			_, basicEnforcementVendor = basicEnforcementVendors[name]
		}

		enforceAlgo := cfg.PurposeEnforcementAlgo(purpose)
		downgraded := isDowngraded(enforceAlgo, basicEnforcementVendor)

		if enforceAlgo == config.TCF2BasicEnforcement || downgraded {
			if cachedEnforcers[index].Basic != nil {
				return cachedEnforcers[index].Basic
			}

			purposeCfg := purposeConfig{
				PurposeID:                  purpose,
				EnforceAlgo:                enforceAlgo,
				EnforcePurpose:             cfg.PurposeEnforced(purpose),
				EnforceVendors:             cfg.PurposeEnforcingVendors(purpose),
				VendorExceptionMap:         cfg.PurposeVendorExceptions(purpose),
				BasicEnforcementVendorsMap: cfg.BasicEnforcementVendors(),
			}

			enforcer := &BasicEnforcement{
				cfg: purposeCfg,
			}
			cachedEnforcers[index].Basic = enforcer
			return enforcer
		} else {
			if cachedEnforcers[index].Full != nil {
				return cachedEnforcers[index].Full
			}

			purposeCfg := purposeConfig{
				PurposeID:                  purpose,
				EnforceAlgo:                enforceAlgo,
				EnforcePurpose:             cfg.PurposeEnforced(purpose),
				EnforceVendors:             cfg.PurposeEnforcingVendors(purpose),
				VendorExceptionMap:         cfg.PurposeVendorExceptions(purpose),
				BasicEnforcementVendorsMap: cfg.BasicEnforcementVendors(),
			}

			enforcer := &FullEnforcement{
				cfg: purposeCfg,
			}
			cachedEnforcers[index].Full = enforcer
			return enforcer
		}
	}
}

// isDowngraded determines if the enforcement algorithm used to determine legal basis for a
// purpose should be downgraded from full enforcement to basic
func isDowngraded(enforceAlgo config.TCF2EnforcementAlgo, basicEnforcementVendor bool) bool {
	if enforceAlgo == config.TCF2FullEnforcement && basicEnforcementVendor {
		return true
	}
	return false
}
