import {
	FEATURE_JETPACK_SEARCH,
	FEATURE_JETPACK_SEARCH_MONTHLY,
	GROUP_JETPACK,
	GROUP_P2,
	GROUP_WPCOM,
	PLAN_A4A_BUSINESS,
	PLAN_A4A_BUSINESS_MONTHLY,
	PLAN_HOSTING_TRIAL_MONTHLY,
	PLAN_MIGRATION_TRIAL_MONTHLY,
	PLAN_WOOEXPRESS_MEDIUM,
	PLAN_WOOEXPRESS_MEDIUM_MONTHLY,
	PLAN_WOOEXPRESS_PLUS,
	PLAN_WOOEXPRESS_SMALL,
	PLAN_WOOEXPRESS_SMALL_MONTHLY,
	PLAN_WOO_HOSTED_FREE,
	PLAN_WOO_HOSTED_FREE_TRIAL_MONTHLY,
	PLAN_WOO_HOSTED_BASIC,
	PLAN_WOO_HOSTED_BASIC_MONTHLY,
	PLAN_WOO_HOSTED_PRO,
	PLAN_WOO_HOSTED_PRO_MONTHLY,
	TERM_ANNUALLY,
	TERM_BIENNIALLY,
	TERM_CENTENNIALLY,
	TERM_DECENNIALLY,
	TERM_MONTHLY,
	TERM_NOVENNIALLY,
	TERM_OCTENNIALLY,
	TERM_QUADRENNIALLY,
	TERM_QUINQUENNIALLY,
	TERM_SEPTENNIALLY,
	TERM_SEXENNIALLY,
	TERM_TRIENNIALLY,
	TYPE_100_YEAR,
	TYPE_ALL,
	TYPE_BLOGGER,
	TYPE_BUSINESS,
	TYPE_ECOMMERCE,
	TYPE_ENTERPRISE_GRID_WPCOM,
	TYPE_FLEXIBLE,
	TYPE_FREE,
	TYPE_JETPACK_STARTER,
	TYPE_P2_PLUS,
	TYPE_PERSONAL,
	TYPE_PREMIUM,
	TYPE_PRO,
	TYPE_SECURITY_DAILY,
	TYPE_SECURITY_REALTIME,
	TYPE_SECURITY_T0,
	TYPE_SECURITY_T1,
	TYPE_SECURITY_T2,
	TYPE_STARTER,
	TYPE_STUDENT,
	TYPE_JETPACK_GROWTH,
	WOO_EXPRESS_PLANS,
	WOO_HOSTED_PLANS,
} from './constants';
import {
	resolveFeatureGroupsForComparisonGrid,
	resolveFeatureGroupsForFeaturesGrid,
	resolveWooExpressFeatureGroupsForComparisonGrid,
	resolveWooHostedFeatureGroupsForFeaturesGrid,
	resolveWooHostedFeatureGroupsForComparisonGrid,
	resolveWordPressHostingFeatureGroupsForFeaturesGrid,
} from './feature-group-plan-map';
import { FEATURES_LIST } from './features-list';
import { PLANS_LIST } from './plans-list';
import type {
	FeatureGroupMap,
	FeatureList,
	JetpackPlan,
	Plan,
	PlanMatchesQuery,
	PlanSlug,
	WPComPlan,
} from './types';
import type { TranslateResult } from 'i18n-calypso';

export function getPlans(): Record< string, Plan > {
	return PLANS_LIST;
}

export function getSimplifiedPlanFeaturesGroupedForFeaturesGrid() {
	return resolveFeatureGroupsForFeaturesGrid( {
		showSimplifiedFeatures: true,
	} );
}

export function getPlanFeaturesGroupedForFeaturesGrid(): Partial< FeatureGroupMap > {
	return resolveFeatureGroupsForFeaturesGrid();
}

export function getPlanFeaturesGroupedForComparisonGrid( props?: {
	isExperimentVariant?: boolean;
} ): Partial< FeatureGroupMap > {
	return resolveFeatureGroupsForComparisonGrid( props );
}

export function getWooExpressFeaturesGroupedForFeaturesGrid(): Partial< FeatureGroupMap > {
	// Same as getPlanFeaturesGroupedForFeaturesGrid() for now
	return getPlanFeaturesGroupedForFeaturesGrid();
}

export function getWordPressHostingFeaturesGroupedForFeaturesGrid(): Partial< FeatureGroupMap > {
	return resolveWordPressHostingFeatureGroupsForFeaturesGrid();
}

export function getWooExpressFeaturesGroupedForComparisonGrid(): Partial< FeatureGroupMap > {
	return resolveWooExpressFeatureGroupsForComparisonGrid();
}

export function getWooHostedFeaturesGroupedForFeaturesGrid(): Partial< FeatureGroupMap > {
	return resolveWooHostedFeatureGroupsForFeaturesGrid();
}

export function getWooHostedFeaturesGroupedForComparisonGrid(): Partial< FeatureGroupMap > {
	return resolveWooHostedFeatureGroupsForComparisonGrid();
}

export function getPlansSlugs(): string[] {
	return Object.keys( getPlans() );
}

export function getPlan( planKey: string | Plan ): Plan | JetpackPlan | WPComPlan | undefined {
	if ( typeof planKey !== 'string' ) {
		if ( Object.values( PLANS_LIST ).includes( planKey ) ) {
			return planKey;
		}
		return undefined;
	}
	return PLANS_LIST[ planKey ];
}

export function getPlanClass( planKey: string ): string {
	if ( isFreePlan( planKey ) ) {
		return 'is-free-plan';
	}

	if ( isFlexiblePlan( planKey ) ) {
		return 'is-flexible-plan';
	}

	if ( isBloggerPlan( planKey ) ) {
		return 'is-blogger-plan';
	}

	if ( isPersonalPlan( planKey ) ) {
		return 'is-personal-plan';
	}

	if ( isStudentPlan( planKey ) ) {
		return 'is-student-plan';
	}

	if ( isPremiumPlan( planKey ) ) {
		return 'is-premium-plan';
	}

	if ( isBusinessPlan( planKey ) ) {
		return 'is-business-plan';
	}

	if ( isWooExpressPlusPlan( planKey ) ) {
		return 'is-wooexpress-plus-plan';
	}

	if ( isWooExpressMediumPlan( planKey ) ) {
		return 'is-wooexpress-medium-plan';
	}

	if ( isWooExpressSmallPlan( planKey ) ) {
		return 'is-wooexpress-small-plan';
	}

	if ( isEcommercePlan( planKey ) ) {
		return 'is-ecommerce-plan';
	}

	if ( isWooHostedFreePlan( planKey ) ) {
		return 'is-woo-hosted-trial';
	}

	if ( isWooHostedBasicPlan( planKey ) ) {
		return 'is-woo-hosted-basic-plan';
	}

	if ( isWooHostedProPlan( planKey ) ) {
		return 'is-woo-hosted-pro-plan';
	}

	if ( isWpcomEnterpriseGridPlan( planKey ) ) {
		return 'is-wpcom-enterprise-grid-plan';
	}

	if ( isProPlan( planKey ) ) {
		return 'is-pro-plan';
	}

	if ( isSecurityDailyPlan( planKey ) ) {
		return 'is-daily-security-plan';
	}

	if ( isSecurityRealTimePlan( planKey ) ) {
		return 'is-realtime-security-plan';
	}

	if ( isSecurityT0Plan( planKey ) ) {
		return 'is-security-t0';
	}

	if ( isSecurityT1Plan( planKey ) ) {
		return 'is-security-t1';
	}

	if ( isSecurityT2Plan( planKey ) ) {
		return 'is-security-t2';
	}

	if ( isCompletePlan( planKey ) ) {
		return 'is-complete-plan';
	}

	if ( isJetpackGrowthPlan( planKey ) ) {
		return 'is-jetpack-growth-plan';
	}

	if ( isFreeHostingTrial( planKey ) ) {
		return 'is-free-hosting-trial';
	}

	if ( isP2PlusPlan( planKey ) ) {
		return 'is-p2-plus-plan';
	}

	return '';
}

/**
 * Determines if a plan has a specific feature.
 *
 * Collects features for a plan by calling all possible feature methods for the plan.
 */
export function planHasFeature( plan: string | Plan, feature: string ): boolean {
	const allFeatures = getAllFeaturesForPlan( plan );
	return allFeatures.includes( feature );
}

/**
 * Determine if a plan has at least one of several features.
 */
export function planHasAtLeastOneFeature( plan: string | Plan, features: string[] ): boolean {
	const allFeatures = getAllFeaturesForPlan( plan );
	return features.some( ( feature ) => allFeatures.includes( feature ) );
}

/**
 * Get all features for a plan
 *
 * Collects features for a plan by calling all possible feature methods for the plan.
 *
 * Returns an array of all the plan features (may have duplicates)
 */
export function getAllFeaturesForPlan( plan: Plan | string ): TranslateResult[] {
	const planObj = getPlan( plan );
	if ( ! planObj ) {
		return [];
	}
	return [
		...( 'getPlanCompareFeatures' in planObj && planObj.getPlanCompareFeatures
			? planObj.getPlanCompareFeatures()
			: [] ),
		...( 'getPromotedFeatures' in planObj && planObj.getPromotedFeatures
			? planObj.getPromotedFeatures()
			: [] ),
		...( 'getSignupFeatures' in planObj && planObj.getSignupFeatures
			? planObj.getSignupFeatures()
			: [] ),
		...( 'getSignupCompareAvailableFeatures' in planObj && planObj.getSignupCompareAvailableFeatures
			? planObj.getSignupCompareAvailableFeatures()
			: [] ),
		...( 'getBlogSignupFeatures' in planObj && planObj.getBlogSignupFeatures
			? planObj.getBlogSignupFeatures()
			: [] ),
		...( 'getPortfolioSignupFeatures' in planObj && planObj.getPortfolioSignupFeatures
			? planObj.getPortfolioSignupFeatures()
			: [] ),
		...( 'getIncludedFeatures' in planObj && planObj.getIncludedFeatures
			? planObj.getIncludedFeatures()
			: [] ),
	];
}

/**
 * Determines if a plan has a superior version of a specific feature.
 */
export function planHasSuperiorFeature( plan: string | Plan, feature: string ): boolean {
	const planConstantObj = getPlan( plan );
	const features = planConstantObj?.getInferiorFeatures?.() ?? [];
	return ( features as ReadonlyArray< string > ).includes( feature );
}

export function shouldFetchSitePlans( sitePlans: {
	hasLoadedFromServer: boolean;
	isRequesting: boolean;
} ): boolean {
	return ! sitePlans.hasLoadedFromServer && ! sitePlans.isRequesting;
}

/**
 * Returns the monthly slug which corresponds to the provided yearly slug or "" if the slug is
 * not a recognized or cannot be converted.
 */
export function getMonthlyPlanByYearly( planSlug: string ): string {
	const plan = getPlan( planSlug );
	if ( plan && 'getMonthlySlug' in plan && plan.getMonthlySlug ) {
		return plan.getMonthlySlug();
	}
	return findFirstSimilarPlanKey( planSlug, { term: TERM_MONTHLY } ) || '';
}

/**
 * Returns the yearly slug which corresponds to the provided monthly slug or "" if the slug is
 * not a recognized or cannot be converted.
 */
export function getYearlyPlanByMonthly( planSlug: string ): string {
	const plan = getPlan( planSlug );
	if ( plan && 'getAnnualSlug' in plan && plan.getAnnualSlug ) {
		return plan.getAnnualSlug();
	}
	return findFirstSimilarPlanKey( planSlug, { term: TERM_ANNUALLY } ) || '';
}

/**
 * Returns true if plan "types" match regardless of their interval.
 *
 * For example (fake plans):
 *     planLevelsMatch( PRO_YEARLY, PRO_YEARLY ) => true
 *     planLevelsMatch( PRO_YEARLY, PRO_MONTHLY ) => true
 *     planLevelsMatch( PRO_YEARLY, PERSONAL_YEARLY ) => false
 */
export function planLevelsMatch( planSlugA: string, planSlugB: string ): boolean {
	const planA = getPlan( planSlugA );
	const planB = getPlan( planSlugB );
	return Boolean( planA && planB && planA.type === planB.type && planA.group === planB.group );
}

export function isEcommercePlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_ECOMMERCE } );
}

export function isProPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_PRO } );
}

export function isBusinessPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_BUSINESS } );
}

export function isPremiumPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_PREMIUM } );
}

export function isPersonalPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_PERSONAL } );
}

export function isStudentPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_STUDENT } );
}

export function isBloggerPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_BLOGGER } );
}

export function isFreePlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_FREE } );
}

export function isFreeHostingTrial( planSlug: string ): boolean {
	return planSlug === PLAN_HOSTING_TRIAL_MONTHLY;
}

export function isBusinessTrial( planSlug: string ): boolean {
	return planSlug === PLAN_HOSTING_TRIAL_MONTHLY || planSlug === PLAN_MIGRATION_TRIAL_MONTHLY;
}

export function is100YearPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_100_YEAR } );
}

// Checks if it is an Enterprise plan (a.k.a VIP), introduced as part of pdgrnI-1Qp-p2.
// This is not a real plan, but added to display Enterprise in the pricing grid.
export function isWpcomEnterpriseGridPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_ENTERPRISE_GRID_WPCOM, group: GROUP_WPCOM } );
}

export function isWooExpressPlusPlan( planSlug: string ): boolean {
	return PLAN_WOOEXPRESS_PLUS === planSlug;
}

export function isWooExpressMediumPlan( planSlug: string ): boolean {
	return [ PLAN_WOOEXPRESS_MEDIUM, PLAN_WOOEXPRESS_MEDIUM_MONTHLY ].includes( planSlug );
}

export function isWooExpressSmallPlan( planSlug: string ): boolean {
	return [ PLAN_WOOEXPRESS_SMALL, PLAN_WOOEXPRESS_SMALL_MONTHLY ].includes( planSlug );
}

export function isWooExpressPlan( planSlug: string ): boolean {
	return ( WOO_EXPRESS_PLANS as ReadonlyArray< string > ).includes( planSlug );
}

export function isWooHostedFreePlan( planSlug: string ): boolean {
	return [ PLAN_WOO_HOSTED_FREE, PLAN_WOO_HOSTED_FREE_TRIAL_MONTHLY ].includes( planSlug );
}

export function isWooHostedBasicPlan( planSlug: string ): boolean {
	return [ PLAN_WOO_HOSTED_BASIC, PLAN_WOO_HOSTED_BASIC_MONTHLY ].includes( planSlug );
}

export function isWooHostedProPlan( planSlug: string ): boolean {
	return [ PLAN_WOO_HOSTED_PRO, PLAN_WOO_HOSTED_PRO_MONTHLY ].includes( planSlug );
}

export function isWooHostedPlan( planSlug: string ): boolean {
	return ( WOO_HOSTED_PLANS as ReadonlyArray< string > ).includes( planSlug );
}

export function isFlexiblePlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_FLEXIBLE } );
}

export function isStarterPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_STARTER } );
}

export function isJetpackStarterPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_JETPACK_STARTER } );
}

export function isJetpackGrowthPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_JETPACK_GROWTH } );
}

export function isSecurityDailyPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_SECURITY_DAILY } );
}

export function isSecurityRealTimePlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_SECURITY_REALTIME } );
}

export function isSecurityT0Plan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_SECURITY_T0 } );
}

export function isSecurityT1Plan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_SECURITY_T1 } );
}

export function isSecurityT2Plan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_SECURITY_T2 } );
}

export function isCompletePlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_ALL } );
}

export function isWpComPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { group: GROUP_WPCOM } );
}

export function isWpComBusinessPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_BUSINESS, group: GROUP_WPCOM } );
}

export function isWpComEcommercePlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_ECOMMERCE, group: GROUP_WPCOM } );
}

export function isWpComProPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_PRO, group: GROUP_WPCOM } );
}

export function isWpComPremiumPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_PREMIUM, group: GROUP_WPCOM } );
}

export function isWpComPersonalPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_PERSONAL, group: GROUP_WPCOM } );
}

export function isWpComStudentPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_STUDENT, group: GROUP_WPCOM } );
}

export function isWpComBloggerPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_BLOGGER, group: GROUP_WPCOM } );
}

export function isWpComFreePlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_FREE, group: GROUP_WPCOM } );
}

export function isWpComAnnualPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { term: TERM_ANNUALLY, group: GROUP_WPCOM } );
}

export function isWpComBiennialPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { term: TERM_BIENNIALLY, group: GROUP_WPCOM } );
}

export function isWpComTriennialPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { term: TERM_TRIENNIALLY, group: GROUP_WPCOM } );
}

export function isWpComMonthlyPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { term: TERM_MONTHLY, group: GROUP_WPCOM } );
}

export function isJetpackBusinessPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_BUSINESS, group: GROUP_JETPACK } );
}

export function isJetpackPremiumPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_PREMIUM, group: GROUP_JETPACK } );
}

export function isJetpackPersonalPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_PERSONAL, group: GROUP_JETPACK } );
}

export function isJetpackFreePlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_FREE, group: GROUP_JETPACK } );
}

export function isP2FreePlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_FREE, group: GROUP_P2 } );
}

export function isP2PlusPlan( planSlug: string ): boolean {
	return planMatches( planSlug, { type: TYPE_P2_PLUS } );
}

export function findFirstSimilarPlanKey(
	planKey: string | Plan,
	diff: PlanMatchesQuery
): string | undefined {
	return findSimilarPlansKeys( planKey, diff )[ 0 ];
}

/**
 * A similar plan is one that has the same `type`, `group`, and `term` as first
 * argument, except for differences specified in the second argument.
 *
 * For example:
 *
 * > findSimilarPlansKeys( TYPE_BUSINESS, { term: TERM_BIENNIALLY } );
 * [PLAN_BUSINESS_2_YEARS]
 * > findSimilarPlansKeys( TYPE_JETPACK_BUSINESS_MONTHLY, { type: TYPE_ANNUALLY } );
 * [TYPE_JETPACK_BUSINESS]
 *
 * Note: A4A plans are excluded from similar plan lookups.
 */
export function findSimilarPlansKeys(
	planKey: string | Plan,
	diff: PlanMatchesQuery = {}
): string[] {
	const plan = getPlan( planKey );
	// @TODO: make getPlan() throw an error on failure. This is going to be a larger change with a separate PR.
	if ( ! plan ) {
		return [];
	}

	const similarPlans = findPlansKeys( {
		type: plan.type,
		group: plan.group,
		term: plan.term,
		...diff,
	} );

	// Filter out A4A plans
	return similarPlans.filter(
		( slug ) => slug !== PLAN_A4A_BUSINESS && slug !== PLAN_A4A_BUSINESS_MONTHLY
	);
}

/**
 * Finds all keys of plans matching a query
 *
 * For example:
 *
 * > findPlansKeys( { term: TERM_BIENNIALLY } );
 * [PLAN_PERSONAL_2_YEARS, PLAN_PREMIUM_2_YEARS, PLAN_BUSINESS_2_YEARS]
 */
export function findPlansKeys( query: PlanMatchesQuery = {} ): string[] {
	const plans = getPlans();
	return Object.keys( plans ).filter( ( k ) => planMatches( plans[ k ], query ) );
}

/**
 * Matches plan specified by `planKey` against `query`.
 * Only compares `type`, `group`, and `term` properties.
 *
 * For example:
 *
 * > planMatches( TYPE_BUSINESS, { term: TERM_ANNUALLY, group: GROUP_WPCOM, type: TYPE_BUSINESS } );
 * true
 *
 * > planMatches( TYPE_BUSINESS, { term: TERM_BIENNIALLY } );
 * false
 */
export function planMatches( planKey: string | Plan, query: PlanMatchesQuery = {} ): boolean {
	const acceptedKeys = [ 'type', 'group', 'term' ];
	const unknownKeys = Object.keys( query ).filter( ( key ) => ! acceptedKeys.includes( key ) );
	if ( unknownKeys.length ) {
		throw new Error(
			`planMatches can only match against ${ acceptedKeys.join( ',' ) }, ` +
				`but unknown keys ${ unknownKeys.join( ',' ) } were passed.`
		);
	}

	// @TODO: make getPlan() throw an error on failure. This is going to be a larger change with a separate PR.
	const plan = getPlan( planKey );
	if ( ! plan ) {
		return false;
	}
	if (
		( ! ( 'type' in query ) || plan.type === query.type ) &&
		( ! ( 'group' in query ) || plan.group === query.group ) &&
		( ! ( 'term' in query ) || plan.term === query.term )
	) {
		return true;
	}
	return false;
}

export function calculateMonthlyPriceForPlan( planSlug: string, termPrice: number ): number {
	const plan = getPlan( planSlug );
	if ( ! plan ) {
		throw new Error( `Unknown plan: ${ planSlug }` );
	}
	return calculateMonthlyPrice( plan.term, termPrice );
}

export function calculateMonthlyPrice( term: string, termPrice: number ): number {
	const divisor = getBillingMonthsForTerm( term );
	return parseFloat( ( termPrice / divisor ).toFixed( 2 ) );
}

export function getBillingMonthsForTerm( term: string ): number {
	if ( term === TERM_MONTHLY ) {
		return 1;
	} else if ( term === TERM_ANNUALLY ) {
		return 12;
	} else if ( term === TERM_BIENNIALLY ) {
		return 24;
	} else if ( term === TERM_TRIENNIALLY ) {
		return 36;
	} else if ( term === TERM_QUADRENNIALLY ) {
		return 48;
	} else if ( term === TERM_QUINQUENNIALLY ) {
		return 60;
	} else if ( term === TERM_SEXENNIALLY ) {
		return 72;
	} else if ( term === TERM_SEPTENNIALLY ) {
		return 84;
	} else if ( term === TERM_OCTENNIALLY ) {
		return 96;
	} else if ( term === TERM_NOVENNIALLY ) {
		return 108;
	} else if ( term === TERM_DECENNIALLY ) {
		return 120;
	} else if ( term === TERM_CENTENNIALLY ) {
		return 1200;
	}
	throw new Error( `Unknown term: ${ term }` );
}

export function getBillingTermForMonths( term: number ): string {
	if ( term === 1 ) {
		return TERM_MONTHLY;
	} else if ( term === 12 ) {
		return TERM_ANNUALLY;
	} else if ( term === 24 ) {
		return TERM_BIENNIALLY;
	} else if ( term === 36 ) {
		return TERM_TRIENNIALLY;
	} else if ( term === 48 ) {
		return TERM_QUADRENNIALLY;
	} else if ( term === 60 ) {
		return TERM_QUINQUENNIALLY;
	} else if ( term === 72 ) {
		return TERM_SEXENNIALLY;
	} else if ( term === 84 ) {
		return TERM_SEPTENNIALLY;
	} else if ( term === 96 ) {
		return TERM_OCTENNIALLY;
	} else if ( term === 108 ) {
		return TERM_NOVENNIALLY;
	} else if ( term === 120 ) {
		return TERM_DECENNIALLY;
	} else if ( term === 1200 ) {
		return TERM_CENTENNIALLY;
	}
	throw new Error( `Unknown term: ${ term }` );
}

export function plansLink(
	urlString: string,
	siteSlug: string | undefined | null,
	intervalType: string,
	forceIntervalType = false
): string {
	const url = new URL( urlString, window.location.origin );

	if ( 'monthly' === intervalType || forceIntervalType ) {
		url.pathname += '/' + intervalType;
	}

	if ( siteSlug ) {
		url.pathname += '/' + siteSlug;
	}

	if ( urlString.startsWith( '/' ) ) {
		return url.pathname + url.search;
	}
	return url.toString();
}

export type FilteredPlan = Plan &
	Pick<
		WPComPlan,
		| 'getPlanCompareFeatures'
		| 'getAnnualPlansOnlyFeatures'
		| 'getPlanTagline'
		| 'getNewsletterTagLine'
		| 'getBlogOnboardingTagLine'
		| 'getVisualSplitBusinessFeatures'
		| 'getVisualSplitCommerceFeatures'
	>;

export function applyTestFiltersToPlansList(
	planName: string | Plan,
	abtest: string | undefined,
	extraArgs: Record< string, string | boolean[] > = {}
): FilteredPlan {
	const plan = getPlan( planName );
	if ( ! plan ) {
		throw new Error( `Unknown plan: ${ planName }` );
	}
	const filteredPlanConstantObj = { ...plan };
	const filteredPlanFeaturesConstantList =
		'getPlanCompareFeatures' in plan && plan.getPlanCompareFeatures
			? plan.getPlanCompareFeatures( abtest, extraArgs )
			: [];

	/* eslint-disable @typescript-eslint/no-empty-function */

	// these becomes no-ops when we removed some of the abtest overrides, but
	// we're leaving the code in place for future tests
	const removeDisabledFeatures = () => {};

	const updatePlanDescriptions = () => {};

	const updatePlanFeatures = () => {};

	/* eslint-enable */

	removeDisabledFeatures();
	updatePlanDescriptions();
	updatePlanFeatures();

	return {
		...filteredPlanConstantObj,
		getPlanCompareFeatures: () => filteredPlanFeaturesConstantList,
	};
}

export function getPlanTermLabel(
	planName: string,
	translate: ( text: string ) => string
): string | undefined {
	const plan = getPlan( planName );
	if ( ! plan || ! plan.term ) {
		return;
	}

	switch ( plan.term ) {
		case TERM_MONTHLY:
			return translate( 'Monthly subscription' );
		case TERM_ANNUALLY:
			return translate( 'Annual subscription' );
		case TERM_BIENNIALLY:
			return translate( 'Two year subscription' );
		case TERM_TRIENNIALLY:
			return translate( 'Three year subscription' );
		case TERM_CENTENNIALLY:
			return translate( 'Hundred year subscription' );
	}
}

export const getPopularPlanSpec = ( {
	flowName,
	customerType,
	isJetpack,
	availablePlans,
}: {
	flowName?: string | null;
	customerType: string;
	isJetpack: boolean;
	availablePlans: string[];
} ): false | { type: string; group: string } => {
	// Jetpack doesn't currently highlight "Popular" plans
	if ( isJetpack ) {
		return false;
	}

	if ( availablePlans.length === 0 ) {
		return false;
	}

	const defaultPlan = getPlan( availablePlans[ 0 ] );

	if ( ! defaultPlan ) {
		return false;
	}

	const group = GROUP_WPCOM;

	if ( flowName === 'hosting' ) {
		return {
			type: TYPE_BUSINESS,
			group,
		};
	}

	if ( customerType === 'personal' ) {
		if ( availablePlans.findIndex( isPremiumPlan ) !== -1 ) {
			return {
				type: TYPE_PREMIUM,
				group,
			};
		}
		// when customerType is not personal, default to business
	} else if ( availablePlans.findIndex( isBusinessPlan ) !== -1 ) {
		return {
			type: TYPE_BUSINESS,
			group,
		};
	}

	// finally, just return the default one.
	return {
		type: defaultPlan.type,
		group,
	};
};

function isValueTruthy< T >( value: T ): value is Exclude< T, null | undefined | false | 0 | '' > {
	return !! value;
}

export const chooseDefaultCustomerType = ( {
	currentCustomerType,
	selectedPlan,
	currentPlan,
}: {
	currentCustomerType: string;
	selectedPlan?: string;
	currentPlan: { productSlug: PlanSlug | undefined };
} ): string => {
	if ( currentCustomerType ) {
		return currentCustomerType;
	}

	const group = GROUP_WPCOM;
	const businessPlanSlugs = [
		findPlansKeys( { group, term: TERM_ANNUALLY, type: TYPE_PREMIUM } )[ 0 ],
		findPlansKeys( { group, term: TERM_BIENNIALLY, type: TYPE_PREMIUM } )[ 0 ],
		findPlansKeys( { group, term: TERM_TRIENNIALLY, type: TYPE_PREMIUM } )[ 0 ],
		findPlansKeys( { group, term: TERM_ANNUALLY, type: TYPE_BUSINESS } )[ 0 ],
		findPlansKeys( { group, term: TERM_BIENNIALLY, type: TYPE_BUSINESS } )[ 0 ],
		findPlansKeys( { group, term: TERM_TRIENNIALLY, type: TYPE_BUSINESS } )[ 0 ],
		findPlansKeys( { group, term: TERM_ANNUALLY, type: TYPE_ECOMMERCE } )[ 0 ],
		findPlansKeys( { group, term: TERM_BIENNIALLY, type: TYPE_ECOMMERCE } )[ 0 ],
		findPlansKeys( { group, term: TERM_TRIENNIALLY, type: TYPE_ECOMMERCE } )[ 0 ],
		findPlansKeys( { group, term: TERM_ANNUALLY, type: TYPE_PRO } )[ 0 ],
		findPlansKeys( { group, term: TERM_BIENNIALLY, type: TYPE_PRO } )[ 0 ],
	]
		.map( ( planKey ) => getPlan( planKey ) )
		.filter( isValueTruthy )
		.map( ( plan ) => plan.getStoreSlug() );

	if ( selectedPlan ) {
		return businessPlanSlugs.includes( selectedPlan as PlanSlug ) ? 'business' : 'personal';
	} else if ( currentPlan ) {
		const isPlanInBusinessGroup =
			businessPlanSlugs.indexOf( currentPlan.productSlug as PlanSlug ) !== -1;
		return isPlanInBusinessGroup ? 'business' : 'personal';
	}

	return 'personal';
};

/**
 * Determines if a plan includes Jetpack Search by looking at the plan's features.
 */
export const planHasJetpackSearch = ( planSlug: string ): boolean =>
	planHasFeature( planSlug, FEATURE_JETPACK_SEARCH ) ||
	planHasFeature( planSlug, FEATURE_JETPACK_SEARCH_MONTHLY );

export function getFeaturesList(): FeatureList {
	return FEATURES_LIST;
}

type FeatureMethodParams = { domainName?: string; isExperimentVariant?: boolean };

export const getPlanFeaturesObject = (
	planFeaturesList?: Array< string >,
	isExperimentVariant?: boolean
) => {
	if ( ! planFeaturesList ) {
		return [];
	}
	return planFeaturesList.map( ( featuresConst ) => {
		const feature = FEATURES_LIST[ featuresConst ];
		if ( ! feature || ! isExperimentVariant ) {
			return feature;
		}
		// Wrap feature methods to pass isExperimentVariant parameter
		return {
			...feature,
			getTitle: ( params?: FeatureMethodParams ) =>
				feature.getTitle( { ...params, isExperimentVariant } ),
			...( feature.getDescription && {
				getDescription: ( params?: FeatureMethodParams ) =>
					feature.getDescription!( { ...params, isExperimentVariant } ),
			} ),
		};
	} );
};

export function isValidFeatureKey( feature: string ) {
	return !! FEATURES_LIST[ feature ];
}

export function getFeatureByKey( feature: string ) {
	return FEATURES_LIST[ feature ];
}

export function getFeatureDifference(
	smallerPlan: string,
	biggerPlan: string,
	featureBundleSelector: keyof WPComPlan
) {
	let biggerPlanObject = ( getPlan( biggerPlan ) as WPComPlan )?.[ featureBundleSelector ] as
		| Array< string >
		| ( () => Array< string > );
	let smallerPlanObject = ( getPlan( smallerPlan ) as WPComPlan )?.[ featureBundleSelector ] as
		| Array< string >
		| ( () => Array< string > );

	if ( typeof biggerPlanObject === 'function' ) {
		biggerPlanObject = biggerPlanObject();
	} else if ( ! Array.isArray( biggerPlanObject ) ) {
		biggerPlanObject = [];
	}

	if ( typeof smallerPlanObject === 'function' ) {
		smallerPlanObject = smallerPlanObject();
	} else if ( ! Array.isArray( smallerPlanObject ) ) {
		smallerPlanObject = [];
	}

	const difference = biggerPlanObject.filter(
		( feature ) => ! smallerPlanObject.includes( feature )
	);

	return difference;
}
