/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the Source EULA. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import * as azdata from 'azdata';
import * as vscode from 'vscode';
import * as utils from '../api/utils';
import { MigrationTargetType } from '../api/utils';
import * as contracts from '../service/contracts';
import { MigrationWizardPage } from '../models/migrationWizardPage';
import { MigrationStateModel, PerformanceDataSourceOptions, StateChangeEvent, AssessmentRuleId } from '../models/stateMachine';
import { AssessmentResultsDialog } from '../dialog/assessment/assessmentResultsDialog';
import { SkuRecommendationResultsDialog } from '../dialog/skuRecommendationResults/skuRecommendationResultsDialog';
import { GetAzureRecommendationDialog } from '../dialog/skuRecommendationResults/getAzureRecommendationDialog';
import * as constants from '../constants/strings';
import { EOL } from 'os';
import { IconPath, IconPathHelper } from '../constants/iconPathHelper';
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
import * as styles from '../constants/styles';
import { SkuEditParametersDialog } from '../dialog/skuRecommendationResults/skuEditParametersDialog';
import { logError, TelemetryViews, TelemetryAction, sendSqlMigrationActionEvent, getTelemetryProps } from '../telemetry';
import { TdeConfigurationDialog } from '../dialog/tdeConfiguration/tdeConfigurationDialog';
import { TdeMigrationModel } from '../models/tdeModels';
import { getSourceConnectionProfile } from '../api/sqlUtils';
import { ConfigDialogSetting } from '../models/tdeModels'

export interface Product {
	type: MigrationTargetType;
	name: string,
	icon: IconPath;
}

export class SKURecommendationPage extends MigrationWizardPage {
	private _view!: azdata.ModelView;
	private _igComponent!: azdata.TextComponent;
	private _assessmentStatusIcon!: azdata.ImageComponent;
	private _detailsComponent!: azdata.TextComponent;
	private _skipAssessmentCheckbox!: azdata.CheckBoxComponent;
	private _skipAssessmentSubText!: azdata.TextComponent;
	private _chooseTargetComponent!: azdata.DivContainer;
	private _rbg!: azdata.RadioCardGroupComponent;
	private eventListener!: vscode.Disposable;
	private _rbgLoader!: azdata.LoadingComponent;
	private _progressContainer!: azdata.FlexContainer;
	private _assessmentComponent!: azdata.FlexContainer;
	private _assessmentProgress!: azdata.TextComponent;
	private _assessmentInfo!: azdata.TextComponent;
	private _formContainer!: azdata.ComponentBuilder<azdata.FormContainer, azdata.ComponentProperties>;
	private _assessmentLoader!: azdata.LoadingComponent;
	private _rootContainer!: azdata.FlexContainer;
	private _viewAssessmentsHelperText!: azdata.TextComponent;
	private _databaseSelectedHelperText!: azdata.TextComponent;
	private _tdedatabaseSelectedHelperText!: azdata.TextComponent;

	private _azureRecommendationSectionText!: azdata.TextComponent;

	private _skuGetRecommendationContainer!: azdata.FlexContainer;
	private _azureRecommendationInfoText!: azdata.TextComponent;
	private _getAzureRecommendationButton!: azdata.ButtonComponent;

	private _skuDataCollectionStatusContainer!: azdata.FlexContainer;
	private _skuDataCollectionStatusIcon!: azdata.ImageComponent;
	private _skuDataCollectionStatusText!: azdata.TextComponent;
	private _skuDataCollectionTimerText!: azdata.TextComponent;

	private _skuControlButtonsContainer!: azdata.FlexContainer;
	private _skuStopDataCollectionButton!: azdata.ButtonComponent;
	private _skuRestartDataCollectionButton!: azdata.ButtonComponent;
	private _refreshAzureRecommendationButton!: azdata.ButtonComponent;
	private _skuLastRefreshTimeText!: azdata.TextComponent;

	private _skuEditParametersContainer!: azdata.FlexContainer;
	private _skuScaleFactorText!: azdata.TextComponent;
	private _skuTargetPercentileText!: azdata.TextComponent;
	private _skuEnablePreviewSkuText!: azdata.TextComponent;
	private _skuEnableElasticRecommendationsText!: azdata.TextComponent;

	private assessmentGroupContainer!: azdata.FlexContainer;
	private _tdeInfoContainer!: azdata.FlexContainer;
	private _disposables: vscode.Disposable[] = [];
	private _tdeConfigurationDialog!: TdeConfigurationDialog;
	private _previousMiTdeMigrationConfig: TdeMigrationModel = new TdeMigrationModel(); // avoid null checks
	private _tdeEditButton!: azdata.ButtonComponent;

	private _serverName: string = '';
	private _supportedProducts: Product[] = [
		{
			type: MigrationTargetType.SQLMI,
			name: constants.SKU_RECOMMENDATION_MI_CARD_TEXT,
			icon: IconPathHelper.sqlMiLogo,

		},
		{
			type: MigrationTargetType.SQLVM,
			name: constants.SKU_RECOMMENDATION_VM_CARD_TEXT,
			icon: IconPathHelper.sqlVmLogo
		},
		{
			type: MigrationTargetType.SQLDB,
			name: constants.SKU_RECOMMENDATION_SQLDB_CARD_TEXT,
			icon: IconPathHelper.sqlDatabaseLogo
		}
	];

	constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
		super(wizard, azdata.window.createWizardPage(constants.ASSESSMENT_RESULTS_AND_RECOMMENDATIONS_PAGE_TITLE), migrationStateModel);
	}

	protected async registerContent(view: azdata.ModelView) {
		this._view = view;
		this._igComponent = this.createStatusComponent(view); // The first component giving basic information
		this._assessmentStatusIcon = this._view.modelBuilder.image()
			.withProps({
				iconPath: IconPathHelper.completedMigration,
				iconHeight: 17,
				iconWidth: 17,
				width: 20,
				height: 20
			}).component();
		const igContainer = this._view.modelBuilder.flexContainer()
			.withProps({ CSSStyles: { 'align-items': 'center' } })
			.component();
		igContainer.addItem(this._assessmentStatusIcon, { flex: '0 0 auto' });
		igContainer.addItem(this._igComponent, { flex: '0 0 auto' });

		this._detailsComponent = this.createDetailsComponent(view); // The details of what can be moved
		this._skipAssessmentCheckbox = view.modelBuilder.checkBox()
			.withProps({
				label: constants.SKU_RECOMMENDATION_ASSESSMENT_ERROR_BYPASS,
				checked: false,
				CSSStyles: {
					...styles.SECTION_HEADER_CSS,
					'margin': '10px 0 0 0',
					'display': 'none'
				},
			}).component();
		this._skipAssessmentSubText = view.modelBuilder.text()
			.withProps({
				value: constants.SKU_RECOMMENDATION_ASSESSMENT_ERROR_DETAIL,
				CSSStyles: {
					'margin': '0 0 0 15px',
					'font-size': '13px',
					'color': 'red',
					'width': '590px',
					'display': 'none'
				},
			}).component();

		this._disposables.push(
			this._skipAssessmentCheckbox.onChanged(
				async (value) => await this._setAssessmentState(false, true)));

		const refreshAssessmentButton = this._view.modelBuilder.button()
			.withProps({
				iconPath: IconPathHelper.refresh,
				label: constants.REFRESH_ASSESSMENT_BUTTON_LABEL,
				width: 160,
				height: 24,
				CSSStyles: {
					...styles.BODY_CSS,
					'margin': '12px 0 4px 0'
				}
			}).component();

		this._disposables.push(refreshAssessmentButton.onDidClick(async () => {
			await this.startCardLoading();
			this.migrationStateModel._runAssessments = true;
			await this.constructDetails();
		}));

		const statusContainer = this._view.modelBuilder.flexContainer()
			.withLayout({ flexFlow: 'column' })
			.withItems([
				igContainer,
				this._detailsComponent,
				refreshAssessmentButton,
				this._skipAssessmentCheckbox,
				this._skipAssessmentSubText])
			.withProps({ CSSStyles: { 'margin': '0' } })
			.component();
		this._chooseTargetComponent = await this.createChooseTargetComponent(view);
		const _azureRecommendationsContainer = this.createAzureRecommendationContainer(view);
		this.assessmentGroupContainer = await this.createViewAssessmentsContainer();
		this._tdeInfoContainer = await this.createTdeInfoContainer();
		this._formContainer = view.modelBuilder.formContainer()
			.withFormItems([
				{ component: statusContainer, title: '' },
				{ component: this._chooseTargetComponent },
				{ component: _azureRecommendationsContainer },
				{ component: this.assessmentGroupContainer },
				{ component: this._tdeInfoContainer }])
			.withProps({
				CSSStyles: {
					'display': 'none',
					'padding-top': '0',
				}
			});

		this._assessmentComponent = this._view.modelBuilder.flexContainer()
			.withLayout({ height: '100%', flexFlow: 'column' })
			.withProps({ CSSStyles: { 'margin-left': '30px' } })
			.component();

		this._assessmentComponent.addItem(this.createAssessmentProgress(), { flex: '0 0 auto' });
		this._assessmentComponent.addItem(await this.createAssessmentInfo(), { flex: '0 0 auto' });

		this._rootContainer = this._view.modelBuilder.flexContainer()
			.withLayout({ height: '100%', flexFlow: 'column' })
			.withProps({ ariaLive: 'polite' })
			.component();
		this._rootContainer.addItem(this._assessmentComponent, { flex: '0 0 auto' });
		this._rootContainer.addItem(this._formContainer.component(), { flex: '0 0 auto' });

		this._disposables.push(this._view.onClosed(
			e => this._disposables.forEach(
				d => { try { d.dispose(); } catch { } })));

		await this._view.initializeModel(this._rootContainer);
	}


	private createStatusComponent(view: azdata.ModelView): azdata.TextComponent {
		const component = view.modelBuilder.text()
			.withProps({
				CSSStyles: {
					...styles.SECTION_HEADER_CSS,
					'margin-left': '8px'
				}
			}).component();
		return component;
	}

	private createDetailsComponent(view: azdata.ModelView): azdata.TextComponent {
		const component = view.modelBuilder.text()
			.withProps({ CSSStyles: { ...styles.BODY_CSS } })
			.component();
		return component;
	}

	private async createChooseTargetComponent(view: azdata.ModelView): Promise<azdata.DivContainer> {
		const chooseYourTargetText = this._view.modelBuilder.text().withProps({
			value: constants.SKU_RECOMMENDATION_CHOOSE_A_TARGET,
			CSSStyles: {
				...styles.SECTION_HEADER_CSS,
				'margin': '0'
			}
		}).component();

		const learnMoreLink = this._view.modelBuilder.hyperlink()
			.withProps({
				label: constants.SKU_RECOMMENDATION_CHOOSE_A_TARGET_HELP,
				ariaLabel: constants.SKU_RECOMMENDATION_CHOOSE_A_TARGET_HELP,
				url: 'https://learn.microsoft.com/azure/azure-sql/azure-sql-iaas-vs-paas-what-is-overview',
				showLinkIcon: true,
			}).component();

		this._rbg = this._view!.modelBuilder.radioCardGroup().withProps({
			cards: [],
			iconHeight: '35px',
			iconWidth: '35px',
			cardWidth: '250px',
			cardHeight: '340px',
			iconPosition: 'left',
			ariaLabel: constants.SKU_RECOMMENDATION_CHOOSE_A_TARGET,
			CSSStyles: {
				'margin-top': '0px',
				'margin-left': '-15px',
			}
		}).component();

		this._supportedProducts.forEach((product) => {
			this._rbg.cards.push({
				id: product.type,
				icon: product.icon,
				descriptions: [
					{
						// 0 - CardDescriptionIndex.TARGET_TYPE
						textValue: product.name,
						textStyles: {
							...styles.SECTION_HEADER_CSS
						}
					},
					{
						// 1 - CardDescriptionIndex.ASSESSMENT_RESULTS_SECTION
						textValue: constants.ASSESSMENT_RESULTS.toLocaleUpperCase(),
						textStyles: {
							...styles.LIGHT_LABEL_CSS,
						}
					},
					{
						// 2 - CardDescriptionIndex.ASSESSMENT_STATUS
						textValue: '',
						textStyles: {
							...styles.BODY_CSS,
							'font-weight': '500'
						}
					},
					{
						// 3 - CardDescriptionIndex.ASSESSED_DBS
						textValue: '',
						textStyles: {
							...styles.BODY_CSS,
						}
					},
					{
						// 4 - CardDescriptionIndex.RECOMMENDATION_RESULTS_SECTION
						textValue: constants.RECOMMENDED_CONFIGURATION.toLocaleUpperCase(),
						textStyles: {
							...styles.LIGHT_LABEL_CSS,
							marginBottom: '0',
						}
					},
					{
						// 5 - CardDescriptionIndex.SKU_RECOMMENDATION
						textValue: constants.AZURE_RECOMMENDATION_CARD_NOT_ENABLED,
						textStyles: {
							...styles.BODY_CSS,
						}
					},
					{
						// 6 - CardDescriptionIndex.VM_CONFIGURATIONS
						textValue: '',
						textStyles: {
							...styles.SMALL_NOTE_CSS,
						}
					},
					{
						// 7 - CardDescriptionIndex.VIEW_SKU_DETAILS
						textValue: '',
						linkDisplayValue: '',
						linkStyles: {
							...styles.BODY_CSS,
							'text-decoration': 'none',
						}
					},
				]
			});

			this._disposables.push(
				this._rbg.onLinkClick(async (e: azdata.RadioCardLinkClickEvent) => {
					if (this.hasRecommendations()) {
						if (e.cardId === product.type) {
							const skuRecommendationResultsDialog = new SkuRecommendationResultsDialog(this.migrationStateModel, product.type);
							await skuRecommendationResultsDialog.openDialog(
								e.cardId,
								this.migrationStateModel._skuRecommendationResults.recommendations);
						}
					}
				}));
		});

		this._disposables.push(this._rbg.onSelectionChanged(async (value) => {
			if (value) {
				this.assessmentGroupContainer.display = 'inline';
				this.changeTargetType(value.cardId);
				await this.refreshTdeView();
			}
		}));

		this._rbgLoader = this._view.modelBuilder.loadingComponent()
			.withItem(this._rbg)
			.component();

		const component = this._view.modelBuilder.divContainer()
			.withItems([chooseYourTargetText, learnMoreLink, this._rbgLoader])
			.component();
		return component;
	}

	private async createTdeInfoContainer(): Promise<azdata.FlexContainer> {
		const container = this._view.modelBuilder.flexContainer().withProps({
			CSSStyles: {
				'flex-direction': 'column'
			}
		}).component();

		this._tdeEditButton = this._view.modelBuilder.button().withProps({
			label: constants.TDE_BUTTON_CAPTION,
			width: 180,
			CSSStyles: {
				...styles.BODY_CSS,
				'margin': '0',
			}
		}).component();
		this._tdeConfigurationDialog = new TdeConfigurationDialog(this.migrationStateModel, () => this._onTdeConfigClosed());
		this._disposables.push(this._tdeEditButton.onDidClick(
			async (e) => await this._tdeConfigurationDialog.openDialog()));

		this._tdedatabaseSelectedHelperText = this._view.modelBuilder.text()
			.withProps({
				CSSStyles: { ...styles.BODY_CSS },
				ariaLive: 'polite',
			}).component();

		container.addItems([
			this._tdeEditButton,
			this._tdedatabaseSelectedHelperText]);

		await utils.updateControlDisplay(container, false);

		return container;
	}

	private async createViewAssessmentsContainer(): Promise<azdata.FlexContainer> {
		this._viewAssessmentsHelperText = this._view.modelBuilder.text().withProps({
			value: constants.SKU_RECOMMENDATION_VIEW_ASSESSMENT_MI,
			CSSStyles: { ...styles.SECTION_HEADER_CSS },
			width: WIZARD_INPUT_COMPONENT_WIDTH
		}).component();

		const button = this._view.modelBuilder.button().withProps({
			label: constants.VIEW_SELECT_BUTTON_LABEL,
			width: 100,
			CSSStyles: { 'margin': '12px 0' }
		}).component();

		this._serverName = this.migrationStateModel.serverName || (await getSourceConnectionProfile()).serverName;

		const assessmentTitle = constants.ASSESSMENT_TITLE(this._serverName);
		const miDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, assessmentTitle, this, MigrationTargetType.SQLMI);
		const vmDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, assessmentTitle, this, MigrationTargetType.SQLVM);
		const dbDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, assessmentTitle, this, MigrationTargetType.SQLDB);

		this._disposables.push(button.onDidClick(async (e) => {
			switch (this._rbg.selectedCardId) {
				case MigrationTargetType.SQLVM:
					this._rbg.selectedCardId = MigrationTargetType.SQLVM;
					return await vmDialog.openDialog();
				case MigrationTargetType.SQLMI:
					this._rbg.selectedCardId = MigrationTargetType.SQLMI;
					return await miDialog.openDialog();
				case MigrationTargetType.SQLDB:
					this._rbg.selectedCardId = MigrationTargetType.SQLDB;
					return await dbDialog.openDialog();
			}
		}));

		this._databaseSelectedHelperText = this._view.modelBuilder.text()
			.withProps({
				CSSStyles: { ...styles.BODY_CSS },
				ariaLive: 'polite',
			}).component();

		const container = this._view.modelBuilder.flexContainer()
			.withItems([
				this._viewAssessmentsHelperText,
				button,
				this._databaseSelectedHelperText
			]).withProps({
				'display': 'none'
			}).component();
		return container;
	}

	private changeTargetType(newTargetType: string): void {
		switch (newTargetType) {
			case MigrationTargetType.SQLMI:
				const miDbs = this.migrationStateModel._miDbs.filter(
					db => this.migrationStateModel._databasesForAssessment.findIndex(
						dba => dba === db) >= 0);

				this._viewAssessmentsHelperText.value = constants.SKU_RECOMMENDATION_VIEW_ASSESSMENT_MI;
				this.migrationStateModel._targetType = MigrationTargetType.SQLMI;
				this.migrationStateModel._databasesForMigration = miDbs;
				break;
			case MigrationTargetType.SQLVM:
				const vmDbs = this.migrationStateModel._vmDbs.filter(
					db => this.migrationStateModel._databasesForAssessment.findIndex(
						dba => dba === db) >= 0);

				this._viewAssessmentsHelperText.value = constants.SKU_RECOMMENDATION_VIEW_ASSESSMENT_VM;
				this.migrationStateModel._targetType = MigrationTargetType.SQLVM;
				this.migrationStateModel._databasesForMigration = vmDbs;
				break;
			case MigrationTargetType.SQLDB:
				const dbDbs = this.migrationStateModel._sqldbDbs.filter(
					db => this.migrationStateModel._databasesForAssessment.findIndex(
						dba => dba === db) >= 0);

				this._viewAssessmentsHelperText.value = constants.SKU_RECOMMENDATION_VIEW_ASSESSMENT_SQLDB;
				this.migrationStateModel._targetType = MigrationTargetType.SQLDB;
				this.migrationStateModel._databasesForMigration = dbDbs;
				break;
		}

		this._databaseSelectedHelperText.value = constants.TOTAL_DATABASES_SELECTED(
			this.migrationStateModel._databasesForMigration.length,
			this.migrationStateModel._databasesForAssessment.length);
		this.migrationStateModel.refreshDatabaseBackupPage = true;
	}

	private async constructDetails(): Promise<void> {
		this.wizard.message = {
			text: '',
			level: azdata.window.MessageLevel.Error
		};

		if (this.migrationStateModel._runAssessments) {
			const errors: string[] = [];
			await this._setAssessmentState(true, false);
			try {
				await this.migrationStateModel.getDatabaseAssessments([MigrationTargetType.SQLMI, MigrationTargetType.SQLDB]);
				const assessmentError = this.migrationStateModel._assessmentResults?.assessmentError;
				if (assessmentError) {
					errors.push(`message: ${assessmentError.message}${EOL}stack: ${assessmentError.stack}`);
				}
				if (this.migrationStateModel?._assessmentResults?.errors?.length! > 0) {
					errors.push(...this.migrationStateModel._assessmentResults?.errors?.map(
						e => `message: ${e.message}${EOL}errorSummary: ${e.errorSummary}${EOL}possibleCauses: ${e.possibleCauses}${EOL}guidance: ${e.guidance}${EOL}errorId: ${e.errorId}`)!);
				}
			} catch (e) {
				errors.push(constants.SKU_RECOMMENDATION_ASSESSMENT_UNEXPECTED_ERROR(this._serverName, e));
				logError(TelemetryViews.MigrationWizardSkuRecommendationPage, 'SkuRecommendationUnexpectedError', e);
			} finally {
				this.migrationStateModel._runAssessments = errors.length > 0;
				if (errors.length > 0) {
					this.wizard.message = {
						text: constants.SKU_RECOMMENDATION_ASSESSMENT_ERROR(this._serverName),
						description: errors.join(EOL),
						level: azdata.window.MessageLevel.Error
					};
					this._assessmentStatusIcon.iconPath = IconPathHelper.error;
					this._igComponent.value = constants.ASSESSMENT_FAILED(this._serverName);
					this._detailsComponent.value = constants.SKU_RECOMMENDATION_ASSESSMENT_ERROR(this._serverName);
				} else {
					this._assessmentStatusIcon.iconPath = IconPathHelper.completedMigration;
					this._igComponent.value = constants.ASSESSMENT_COMPLETED(this._serverName);
					this._detailsComponent.value = constants.SKU_RECOMMENDATION_ALL_SUCCESSFUL(
						this.migrationStateModel._assessmentResults?.databaseAssessments?.length);
				}
			}
		} else {
			// use prior assessment results
			this._assessmentStatusIcon.iconPath = IconPathHelper.completedMigration;
			this._igComponent.value = constants.ASSESSMENT_COMPLETED(this._serverName);
			this._detailsComponent.value = constants.SKU_RECOMMENDATION_ALL_SUCCESSFUL(
				this.migrationStateModel._assessmentResults?.databaseAssessments?.length);
		}

		if (this.migrationStateModel.savedInfo?.migrationTargetType) {
			this._rbg.selectedCardId = this.migrationStateModel._targetType;
		}

		let shouldGetSkuRecommendations = false;

		// recommendations were already generated, then the user went back and changed the list of databases
		// so recommendations should be re-generated
		if (this.hasRecommendations() && this.migrationStateModel.hasRecommendedDatabaseListChanged()) {
			shouldGetSkuRecommendations = true;
		}

		if (this.migrationStateModel.savedInfo?.skuRecommendation) {
			await this.refreshSkuParameters();

			switch (this.migrationStateModel._skuRecommendationPerformanceDataSource) {
				case PerformanceDataSourceOptions.CollectData: {
					// check if collector is still running
					await this.migrationStateModel.refreshPerfDataCollection();
					if (this.migrationStateModel._perfDataCollectionIsCollecting) {
						// user started collecting data, ensure the collector is still running
						await this.migrationStateModel.startSkuTimers(this);
						await this.refreshSkuRecommendationComponents();
					} else {
						// user started collecting data, but collector is stopped
						// set stop date to some date value
						this.migrationStateModel._perfDataCollectionStopDate = this.migrationStateModel._perfDataCollectionStopDate || new Date();
						shouldGetSkuRecommendations = true;
					}
					break;
				}

				case PerformanceDataSourceOptions.OpenExisting: {
					shouldGetSkuRecommendations = true;
					break;
				}
			}
		}

		if (shouldGetSkuRecommendations) {
			await this.migrationStateModel.getSkuRecommendations();
		}

		await this.refreshSkuRecommendationComponents();
		await this._setAssessmentState(false, this.migrationStateModel._runAssessments);
	}

	private async _setAssessmentState(assessing: boolean, failedAssessment: boolean): Promise<void> {
		await utils.updateControlDisplay(
			this._assessmentComponent,
			assessing,
			'block');
		await utils.updateControlDisplay(
			this._skipAssessmentCheckbox,
			!assessing && failedAssessment,
			'block');
		await utils.updateControlDisplay(
			this._skipAssessmentSubText,
			!assessing && failedAssessment,
			'block');
		await utils.updateControlDisplay(
			this._formContainer.component(),
			!assessing,
			'block');
		await utils.updateControlDisplay(
			this._chooseTargetComponent,
			!failedAssessment || this._skipAssessmentCheckbox.checked === true,
			'block');
		await utils.updateControlDisplay(
			this.assessmentGroupContainer,
			this._rbg.selectedCardId !== undefined && (!failedAssessment || this._skipAssessmentCheckbox.checked === true),
			'inline');

		this._assessmentLoader.loading = assessing;
	}

	public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
		this.wizard.registerNavigationValidator((pageChangeInfo) => {
			this.wizard.message = { text: '' };
			if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
				return true;
			}

			const errors: string[] = [];
			if (this._rbg.selectedCardId === undefined || this._rbg.selectedCardId === '') {
				errors.push(constants.SELECT_TARGET_TO_CONTINUE);
			}
			if (this.migrationStateModel._databasesForMigration.length === 0) {
				errors.push(constants.SELECT_DATABASE_TO_MIGRATE);
			}

			if (errors.length > 0) {
				this.wizard.message = {
					text: errors.join(EOL),
					level: azdata.window.MessageLevel.Error
				};
				return false;
			}
			return true;
		});
		await this.constructDetails();
		this.wizard.nextButton.enabled = this.migrationStateModel._assessmentResults !== undefined;
		this._previousMiTdeMigrationConfig = this.migrationStateModel.tdeMigrationConfig;
	}

	public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
		this.wizard.registerNavigationValidator(pageChangeInfo => true);
		this.wizard.message = { text: '' };
		this.eventListener?.dispose();
	}

	protected async handleStateChange(e: StateChangeEvent): Promise<void> {
	}

	public async refreshCardText(showLoadingIcon: boolean = true): Promise<void> {
		this._rbgLoader.loading = showLoadingIcon && true;
		switch (this._rbg.selectedCardId) {
			case MigrationTargetType.SQLMI:
				this.migrationStateModel._databasesForMigration = this.migrationStateModel._miDbs;
			case MigrationTargetType.SQLDB:
				this.migrationStateModel._databasesForMigration = this.migrationStateModel._sqldbDbs;
			case MigrationTargetType.SQLVM:
				this.migrationStateModel._databasesForMigration = this.migrationStateModel._vmDbs;
		}

		const dbCount = this.migrationStateModel._assessmentResults?.databaseAssessments?.length;
		const dbWithoutIssuesForMiCount = this.migrationStateModel._assessmentResults?.databaseAssessments?.filter(db =>
			!db.issues?.some(issue => issue.appliesToMigrationTargetPlatform === MigrationTargetType.SQLMI)
		).length;
		const dbWithoutIssuesForVmCount = dbCount;
		const dbWithoutIssuesForDbCount = this.migrationStateModel._assessmentResults?.databaseAssessments?.filter(db =>
			!db.issues?.some(issue => issue.appliesToMigrationTargetPlatform === MigrationTargetType.SQLDB)
		).length;

		this._supportedProducts.forEach((product, index) => {
			if (!this.migrationStateModel._assessmentResults) {
				this._rbg.cards[index].descriptions[CardDescriptionIndex.ASSESSMENT_STATUS].textValue = '';
			} else {
				if (this.hasRecommendations()) {
					this._rbg.cards[index].descriptions[CardDescriptionIndex.VIEW_SKU_DETAILS].linkDisplayValue = constants.VIEW_DETAILS;
					this._rbg.cards[index].descriptions[CardDescriptionIndex.SKU_RECOMMENDATION].textStyles = {
						...styles.BODY_CSS,
						'font-weight': '500',
					};
				} else {
					this._rbg.cards[index].descriptions[CardDescriptionIndex.VIEW_SKU_DETAILS].linkDisplayValue = '';
					this._rbg.cards[index].descriptions[CardDescriptionIndex.SKU_RECOMMENDATION].textStyles = {
						...styles.BODY_CSS,
					};

					if (this.migrationStateModel._perfDataCollectionStartDate) {
						this._rbg.cards[index].descriptions[CardDescriptionIndex.SKU_RECOMMENDATION].textValue =
							constants.AZURE_RECOMMENDATION_CARD_IN_PROGRESS;
					} else {
						this._rbg.cards[index].descriptions[CardDescriptionIndex.SKU_RECOMMENDATION].textValue =
							constants.AZURE_RECOMMENDATION_CARD_NOT_ENABLED;
					}
				}

				let recommendation;
				switch (product.type) {
					case MigrationTargetType.SQLMI:
						this._rbg.cards[index].descriptions[CardDescriptionIndex.ASSESSMENT_STATUS].textValue =
							constants.CAN_BE_MIGRATED(dbWithoutIssuesForMiCount, dbCount);

						if (this.hasRecommendations()) {
							if (this.migrationStateModel._skuEnableElastic) {
								recommendation = this.migrationStateModel._skuRecommendationResults.recommendations?.elasticSqlMiRecommendationResults[0];
							} else {
								recommendation = this.migrationStateModel._skuRecommendationResults.recommendations?.sqlMiRecommendationResults[0];
							}

							// result returned but no SKU recommended
							if (!recommendation?.targetSku) {
								this._rbg.cards[index].descriptions[CardDescriptionIndex.SKU_RECOMMENDATION].textValue =
									constants.SKU_RECOMMENDATION_NO_RECOMMENDATION;
							}
							else {
								const serviceTier = recommendation.targetSku.category?.sqlServiceTier === contracts.AzureSqlPaaSServiceTier.GeneralPurpose
									? constants.GENERAL_PURPOSE
									: constants.BUSINESS_CRITICAL;
								const hardwareType = recommendation.targetSku.category?.hardwareType === contracts.AzureSqlPaaSHardwareType.Gen5
									? constants.GEN5
									: recommendation.targetSku.category?.hardwareType === contracts.AzureSqlPaaSHardwareType.PremiumSeries
										? constants.PREMIUM_SERIES
										: constants.PREMIUM_SERIES_MEMORY_OPTIMIZED;
								this._rbg.cards[index].descriptions[CardDescriptionIndex.SKU_RECOMMENDATION].textValue =
									constants.MI_CONFIGURATION_PREVIEW(
										hardwareType,
										serviceTier,
										recommendation.targetSku.computeSize!,
										recommendation.targetSku.storageMaxSizeInMb! / 1024);
							}
						}
						break;

					case MigrationTargetType.SQLVM:
						this._rbg.cards[index].descriptions[CardDescriptionIndex.ASSESSMENT_STATUS].textValue =
							constants.CAN_BE_MIGRATED(dbWithoutIssuesForVmCount, dbCount);

						if (this.hasRecommendations()) {
							// elastic model currently doesn't support SQL VM, so show the baseline model results regardless of user preference
							recommendation = this.migrationStateModel._skuRecommendationResults.recommendations?.sqlVmRecommendationResults[0];

							// result returned but no SKU recommended
							if (!recommendation?.targetSku) {
								this._rbg.cards[index].descriptions[CardDescriptionIndex.SKU_RECOMMENDATION].textValue =
									constants.SKU_RECOMMENDATION_NO_RECOMMENDATION;
								this._rbg.cards[index].descriptions[CardDescriptionIndex.VM_CONFIGURATIONS].textValue = '';
							}
							else {
								this._rbg.cards[index].descriptions[CardDescriptionIndex.SKU_RECOMMENDATION].textValue =
									constants.VM_CONFIGURATION(
										recommendation.targetSku.virtualMachineSize!.sizeName,
										recommendation.targetSku.virtualMachineSize!.vCPUsAvailable);

								const dataDisk = constants.STORAGE_CONFIGURATION(
									recommendation.targetSku.dataDiskSizes![0].size,
									recommendation.targetSku.dataDiskSizes!.length);
								const storageDisk = constants.STORAGE_CONFIGURATION(
									recommendation.targetSku.logDiskSizes![0].size,
									recommendation.targetSku.logDiskSizes!.length);
								const tempDb = recommendation.targetSku.tempDbDiskSizes!.length > 0
									? constants.STORAGE_CONFIGURATION(
										recommendation.targetSku.logDiskSizes![0].size,
										recommendation.targetSku.logDiskSizes!.length)
									: constants.LOCAL_SSD;
								this._rbg.cards[index].descriptions[CardDescriptionIndex.VM_CONFIGURATIONS].textValue =
									constants.VM_CONFIGURATION_PREVIEW(dataDisk, storageDisk, tempDb);
							}
						}
						break;

					case MigrationTargetType.SQLDB:
						this._rbg.cards[index].descriptions[CardDescriptionIndex.ASSESSMENT_STATUS].textValue =
							constants.CAN_BE_MIGRATED(dbWithoutIssuesForDbCount, dbCount);

						if (this.hasRecommendations()) {
							const recommendations = this.migrationStateModel._skuEnableElastic
								? this.migrationStateModel._skuRecommendationResults.recommendations!.elasticSqlDbRecommendationResults
								: this.migrationStateModel._skuRecommendationResults.recommendations!.sqlDbRecommendationResults;
							const successfulRecommendationsCount = recommendations.filter(r => r.targetSku !== null).length;
							this._rbg.cards[index].descriptions[CardDescriptionIndex.SKU_RECOMMENDATION].textValue =
								constants.RECOMMENDATIONS_AVAILABLE(successfulRecommendationsCount);
						}
						break;
				}
			}
		});

		await this._rbg.updateProperties({ cards: this._rbg.cards });

		if (this._rbg.selectedCardId) {
			this.changeTargetType(this._rbg.selectedCardId);
		}

		await this.refreshTdeView();

		this._rbgLoader.loading = false;
	}


	private _resetTdeConfiguration() {
		this._previousMiTdeMigrationConfig = this.migrationStateModel.tdeMigrationConfig;
		this.migrationStateModel.tdeMigrationConfig = new TdeMigrationModel();
	}

	private async refreshTdeView() {

		if (this.migrationStateModel._targetType !== MigrationTargetType.SQLMI) {

			//Reset the encrypted databases counter on the model to ensure the certificates migration is ignored.
			this._resetTdeConfiguration();

		} else {

			const encryptedDbFound = this.migrationStateModel._assessmentResults.databaseAssessments
				.filter(
					db => this.migrationStateModel._databasesForMigration.findIndex(dba => dba === db.name) >= 0 &&
						db.issues.findIndex(iss => iss.ruleId === AssessmentRuleId.TdeEnabled && iss.appliesToMigrationTargetPlatform === MigrationTargetType.SQLMI) >= 0
				)
				.map(db => db.name);

			if (this._matchWithEncryptedDatabases(encryptedDbFound)) {
				this.migrationStateModel.tdeMigrationConfig = this._previousMiTdeMigrationConfig;
			} else {
				if (!utils.isWindows()) //Only available for windows for now.
					return;

				//Set encrypted databases
				this.migrationStateModel.tdeMigrationConfig.setTdeEnabledDatabasesCount(encryptedDbFound);

				if (this.migrationStateModel.tdeMigrationConfig.hasTdeEnabledDatabases()) {
					//Set the text when there are encrypted databases.

					if (!this.migrationStateModel.tdeMigrationConfig.shownBefore()) {
						await this._tdeConfigurationDialog.openDialog();
					}
				} else {
					this._tdedatabaseSelectedHelperText.value = constants.TDE_WIZARD_MSG_EMPTY;
				}

			}
		}

		await utils.updateControlDisplay(this._tdeInfoContainer, this.migrationStateModel.tdeMigrationConfig.hasTdeEnabledDatabases());
	}

	private _onTdeConfigClosed(): Thenable<void> {
		const tdeMsg = (this.migrationStateModel.tdeMigrationConfig.getAppliedConfigDialogSetting() === ConfigDialogSetting.ExportCertificates) ? constants.TDE_WIZARD_MSG_TDE : constants.TDE_WIZARD_MSG_MANUAL;
		this._tdedatabaseSelectedHelperText.value = constants.TDE_MSG_DATABASES_SELECTED(this.migrationStateModel.tdeMigrationConfig.getTdeEnabledDatabasesCount(), tdeMsg);

		let tdeTelemetryAction: TelemetryAction;

		switch (this.migrationStateModel.tdeMigrationConfig.getAppliedConfigDialogSetting()) {
			case ConfigDialogSetting.ExportCertificates:
				tdeTelemetryAction = TelemetryAction.TdeConfigurationUseADS;
				break;
			case ConfigDialogSetting.DoNotExport:
				tdeTelemetryAction = TelemetryAction.TdeConfigurationAlreadyMigrated;
				break;
			case ConfigDialogSetting.NoSelection:
				tdeTelemetryAction = TelemetryAction.TdeConfigurationCancelled;
				break;
			default:
				tdeTelemetryAction = TelemetryAction.TdeConfigurationCancelled;
				break;
		}

		sendSqlMigrationActionEvent(
			TelemetryViews.TdeConfigurationDialog,
			tdeTelemetryAction,
			{
				...getTelemetryProps(this.migrationStateModel),
				'numberOfDbsWithTde': this.migrationStateModel.tdeMigrationConfig.getTdeEnabledDatabasesCount().toString()
			},
			{}
		);

		return this._tdeEditButton.focus();
	}

	private _matchWithEncryptedDatabases(encryptedDbList: string[]): boolean {
		var currentTdeDbs = this._previousMiTdeMigrationConfig.getTdeEnabledDatabases();

		if (encryptedDbList.length === 0 || encryptedDbList.length !== currentTdeDbs.length)
			return false;

		if (encryptedDbList.filter(db => currentTdeDbs.findIndex(dba => dba === db) < 0).length > 0)
			return false; //There is at least one element that is not in the other array. There should be no risk of duplicates table names


		return true;
	}


	public async startCardLoading(): Promise<void> {
		// TO-DO: ideally the short SKU recommendation loading time should have a spinning indicator,
		// but updating the card text will do for now
		this._supportedProducts.forEach((product, index) => {
			this._rbg.cards[index].descriptions[CardDescriptionIndex.SKU_RECOMMENDATION].textValue = constants.LOADING_RECOMMENDATIONS;
			this._rbg.cards[index].descriptions[CardDescriptionIndex.SKU_RECOMMENDATION].textStyles = { ...styles.BODY_CSS };
			this._rbg.cards[index].descriptions[CardDescriptionIndex.VM_CONFIGURATIONS].textValue = '';
		});

		await this._rbg.updateProperties({ cards: this._rbg.cards });
	}

	private createAssessmentProgress(): azdata.FlexContainer {
		this._assessmentLoader = this._view.modelBuilder.loadingComponent()
			.component();

		this._assessmentProgress = this._view.modelBuilder.text()
			.withProps({
				value: constants.ASSESSMENT_IN_PROGRESS,
				CSSStyles: {
					...styles.PAGE_TITLE_CSS,
					'margin-right': '20px'
				}
			}).component();

		this._progressContainer = this._view.modelBuilder.flexContainer()
			.withLayout({
				height: '100%',
				flexFlow: 'row',
				alignItems: 'center'
			}).component();

		this._progressContainer.addItem(this._assessmentProgress, { flex: '0 0 auto' });
		this._progressContainer.addItem(this._assessmentLoader, { flex: '0 0 auto' });
		return this._progressContainer;
	}

	private async createAssessmentInfo(): Promise<azdata.TextComponent> {
		this._assessmentInfo = this._view.modelBuilder.text()
			.withProps({
				value: constants.ASSESSMENT_IN_PROGRESS_CONTENT(this._serverName),
				CSSStyles: {
					...styles.BODY_CSS,
					'width': '660px'
				}
			}).component();
		return this._assessmentInfo;
	}

	private createAzureRecommendationContainer(_view: azdata.ModelView): azdata.FlexContainer {
		const container = _view.modelBuilder.flexContainer()
			.withProps({
				CSSStyles: {
					'flex-direction': 'column',
					'max-width': '700px',
					'margin-bottom': '1em',
				}
			}).component();
		this._azureRecommendationSectionText = _view.modelBuilder.text()
			.withProps({
				value: constants.AZURE_RECOMMENDATION,
				description: '',
				CSSStyles: {
					...styles.SECTION_HEADER_CSS,
					'margin': '12px 0 8px',
				}
			}).component();
		this._azureRecommendationInfoText = _view.modelBuilder.text()
			.withProps({
				value: constants.AZURE_RECOMMENDATION_STATUS_NOT_ENABLED,
				CSSStyles: {
					...styles.BODY_CSS,
					'margin': '0',
				}
			}).component();
		const learnMoreLink = _view.modelBuilder.hyperlink()
			.withProps({
				label: constants.LEARN_MORE,
				ariaLabel: constants.LEARN_MORE,
				url: 'https://aka.ms/ads-sql-sku-recommend',
				showLinkIcon: true,
			}).component();
		const azureRecommendationsInfoContainer = _view.modelBuilder.flexContainer()
			.withItems([
				this._azureRecommendationInfoText,
				learnMoreLink,
			]).withProps({
				CSSStyles: {
					'flex-direction': 'column',
					'margin-top': '-0.5em',
					'margin-bottom': '12px',
				}
			}).component();
		this._getAzureRecommendationButton = this._view.modelBuilder.button().withProps({
			label: constants.GET_AZURE_RECOMMENDATION,
			width: 180,
			CSSStyles: {
				...styles.BODY_CSS,
				'margin': '0',
			}
		}).component();
		const getAzureRecommendationDialog = new GetAzureRecommendationDialog(this, this.wizard, this.migrationStateModel);
		this._disposables.push(this._getAzureRecommendationButton.onDidClick(
			async (e) => await getAzureRecommendationDialog.openDialog()));

		this._skuGetRecommendationContainer = _view.modelBuilder.flexContainer()
			.withProps({ CSSStyles: { 'flex-direction': 'column', } })
			.component();

		this._skuGetRecommendationContainer.addItems([
			azureRecommendationsInfoContainer,
			this._getAzureRecommendationButton]);

		this._skuDataCollectionStatusContainer = this.createPerformanceCollectionStatusContainer(_view);
		this._skuEditParametersContainer = this.createSkuEditParameters(_view);
		container.addItems([
			this._azureRecommendationSectionText,
			this._skuDataCollectionStatusContainer,
			this._skuGetRecommendationContainer,
			this._skuEditParametersContainer,
		]);
		return container;
	}

	private createPerformanceCollectionStatusContainer(_view: azdata.ModelView): azdata.FlexContainer {
		const container = _view.modelBuilder.flexContainer()
			.withProps({
				CSSStyles: {
					'flex-direction': 'column',
					'display': this.migrationStateModel.performanceCollectionNotStarted() ? 'none' : 'block',
				}
			}).component();

		this._skuDataCollectionStatusIcon = _view.modelBuilder.image()
			.withProps({
				iconPath: IconPathHelper.inProgressMigration,
				iconHeight: 16,
				iconWidth: 16,
				width: 16,
				height: 16,
				CSSStyles: { 'margin-right': '4px' }
			}).component();
		this._skuDataCollectionStatusText = _view.modelBuilder.text()
			.withProps({
				value: '',
				CSSStyles: { ...styles.BODY_CSS, 'margin': '0' }
			}).component();

		const statusIconTextContainer = _view.modelBuilder.flexContainer()
			.withItems([
				this._skuDataCollectionStatusIcon,
				this._skuDataCollectionStatusText,
			])
			.withProps({
				CSSStyles: {
					'flex-direction': 'row',
					'width': 'fit-content',
					'align-items': 'center',
					'margin': '0',
				}
			}).component();

		this._skuDataCollectionTimerText = _view.modelBuilder.text()
			.withProps({
				value: '',
				CSSStyles: {
					...styles.LIGHT_LABEL_CSS,
					'margin': '0 0 8px 20px',
				}
			}).component();

		this._skuStopDataCollectionButton = this._view.modelBuilder.button()
			.withProps({
				iconPath: IconPathHelper.cancel,
				label: constants.STOP_PERFORMANCE_COLLECTION,
				width: 150,
				height: 24,
				CSSStyles: {
					...styles.BODY_CSS,
					'margin': '0',
					'display': this.migrationStateModel.performanceCollectionInProgress() ? 'block' : 'none',
				}
			}).component();
		this._disposables.push(this._skuStopDataCollectionButton.onDidClick(async (e) => {
			await this.migrationStateModel.stopPerfDataCollection();
			await this.refreshAzureRecommendation();
		}));

		this._skuRestartDataCollectionButton = this._view.modelBuilder.button().withProps({
			iconPath: IconPathHelper.restartDataCollection,
			label: constants.RESTART_PERFORMANCE_COLLECTION,
			width: 160,
			height: 24,
			CSSStyles: {
				...styles.BODY_CSS,
				'margin': '0',
				'display': this.migrationStateModel.performanceCollectionStopped() ? 'block' : 'none',
			}
		}).component();
		this._disposables.push(
			this._skuRestartDataCollectionButton.onDidClick(async (e) => {
				await this.migrationStateModel.startPerfDataCollection(
					this.migrationStateModel._skuRecommendationPerformanceLocation,
					this.migrationStateModel._performanceDataQueryIntervalInSeconds,
					this.migrationStateModel._staticDataQueryIntervalInSeconds,
					this.migrationStateModel._numberOfPerformanceDataQueryIterations,
					this);
				await this.refreshSkuRecommendationComponents();
			}));

		this._refreshAzureRecommendationButton = this._view.modelBuilder.button().withProps({
			iconPath: IconPathHelper.refresh,
			label: constants.REFRESH_AZURE_RECOMMENDATION,
			width: 180,
			height: 24,
			CSSStyles: {
				...styles.BODY_CSS,
				'margin': '0 0 0 12px',
			}
		}).component();
		this._disposables.push(
			this._refreshAzureRecommendationButton.onDidClick(
				async (e) => await this.refreshAzureRecommendation()));

		this._skuLastRefreshTimeText = this._view.modelBuilder.text()
			.withProps({
				value: constants.LAST_REFRESHED_TIME(),
				CSSStyles: {
					...styles.SMALL_NOTE_CSS,
					'margin': '0 0 4px 4px',
				},
			}).component();
		this._skuControlButtonsContainer = _view.modelBuilder.flexContainer()
			.withProps({
				CSSStyles: {
					'flex-direction': 'row',
					'width': 'fit-content',
					'align-items': 'flex-end',
					'margin-bottom': '12px',
				}
			}).component();
		this._skuControlButtonsContainer.addItems([
			this._skuStopDataCollectionButton,
			this._skuRestartDataCollectionButton,
			this._refreshAzureRecommendationButton,
			this._skuLastRefreshTimeText]);

		container.addItems([
			this._skuControlButtonsContainer,
			statusIconTextContainer,
			this._skuDataCollectionTimerText]);

		return container;
	}

	private createSkuEditParameters(_view: azdata.ModelView): azdata.FlexContainer {
		const container = _view.modelBuilder.flexContainer().withProps({
			CSSStyles: {
				'flex-direction': 'column',
				'display': this.migrationStateModel.performanceCollectionNotStarted() ? 'none' : 'block',
			}
		}).component();
		const recommendationParametersSection = _view.modelBuilder.text()
			.withProps({
				value: constants.RECOMMENDATION_PARAMETERS,
				CSSStyles: {
					...styles.BODY_CSS,
					'margin': '16px 0 8px'
				}
			}).component();

		const editParametersButton = this._view.modelBuilder.button()
			.withProps({
				iconPath: IconPathHelper.edit,
				label: constants.EDIT_PARAMETERS,
				width: 130,
				height: 24,
				CSSStyles: {
					...styles.BODY_CSS,
					'margin': '0',
					'width': 'fit-content',
				}
			}).component();
		let skuEditParametersDialog = new SkuEditParametersDialog(this, this.migrationStateModel);
		this._disposables.push(
			editParametersButton.onDidClick(
				async () => await skuEditParametersDialog.openDialog()));

		const createParameterGroup = (label: string, value: string): {
			flexContainer: azdata.FlexContainer,
			text: azdata.TextComponent,
		} => {
			const parameterGroup = this._view.modelBuilder.flexContainer()
				.withProps({
					CSSStyles: {
						'flex-direction': 'row',
						'align-content': 'left',
						'width': 'fit-content',
						'margin-right': '24px',
					}
				}).component();
			const labelText = this._view.modelBuilder.text()
				.withProps({
					value: label + ':',
					CSSStyles: {
						...styles.LIGHT_LABEL_CSS,
						'width': 'fit-content',
						'margin-right': '4px',
					}
				}).component();
			const valueText = this._view.modelBuilder.text()
				.withProps({
					value: value,
					CSSStyles: {
						...styles.BODY_CSS,
						'width': 'fit-content,',
					}
				}).component();
			parameterGroup.addItems([
				labelText,
				valueText]);
			return {
				flexContainer: parameterGroup,
				text: valueText,
			};
		};

		const scaleFactorParameterGroup = createParameterGroup(
			constants.SCALE_FACTOR,
			this.migrationStateModel._skuScalingFactor.toString());
		this._skuScaleFactorText = scaleFactorParameterGroup.text;

		const skuTargetPercentileParameterGroup = createParameterGroup(
			constants.PERCENTAGE_UTILIZATION,
			constants.PERCENTAGE(this.migrationStateModel._skuTargetPercentile));
		this._skuTargetPercentileText = skuTargetPercentileParameterGroup.text;

		const skuEnablePreviewParameterGroup = createParameterGroup(
			constants.ENABLE_PREVIEW_SKU,
			this.migrationStateModel._skuEnablePreview ? constants.YES : constants.NO);
		this._skuEnablePreviewSkuText = skuEnablePreviewParameterGroup.text;

		const skuEnableElasticRecommendationsParameterGroup = createParameterGroup(constants.ELASTIC_RECOMMENDATION_LABEL, this.migrationStateModel._skuEnableElastic ? constants.YES : constants.NO);
		this._skuEnableElasticRecommendationsText = skuEnableElasticRecommendationsParameterGroup.text;

		const parametersContainer = _view.modelBuilder.flexContainer()
			.withProps({
				CSSStyles: {
					'margin': '8px 0',
					'flex-direction': 'row',
					'width': 'fit-content',
				}
			}).component();
		parametersContainer.addItems([
			scaleFactorParameterGroup.flexContainer,
			skuTargetPercentileParameterGroup.flexContainer,
			skuEnablePreviewParameterGroup.flexContainer,
			skuEnableElasticRecommendationsParameterGroup.flexContainer
		]);

		container.addItems([
			recommendationParametersSection,
			editParametersButton,
			parametersContainer,
		]);
		return container;
	}

	public async refreshSkuParameters(): Promise<void> {
		this._skuScaleFactorText.value = this.migrationStateModel._skuScalingFactor.toString();
		this._skuTargetPercentileText.value = constants.PERCENTAGE(this.migrationStateModel._skuTargetPercentile);
		this._skuEnablePreviewSkuText.value = this.migrationStateModel._skuEnablePreview ? constants.YES : constants.NO;
		this._skuEnableElasticRecommendationsText.value = this.migrationStateModel._skuEnableElastic ? constants.YES : constants.NO;
	}

	public async refreshAzureRecommendation(): Promise<void> {
		await this.startCardLoading();
		this._skuLastRefreshTimeText.value = constants.LAST_REFRESHED_TIME();
		await this.migrationStateModel.getSkuRecommendations();

		const skuRecommendationError = this.migrationStateModel._skuRecommendationResults?.recommendationError;
		if (skuRecommendationError) {
			this.wizard.message = {
				text: constants.SKU_RECOMMENDATION_ERROR(this._serverName),
				description: skuRecommendationError.message,
				level: azdata.window.MessageLevel.Error
			};
		}

		await this.refreshSkuRecommendationComponents();
		this._skuLastRefreshTimeText.value = constants.LAST_REFRESHED_TIME(new Date().toLocaleString());
	}

	public async refreshSkuRecommendationComponents(): Promise<void> {
		switch (this.migrationStateModel._skuRecommendationPerformanceDataSource) {
			case PerformanceDataSourceOptions.CollectData: {
				await this._azureRecommendationSectionText.updateProperties({
					description: constants.AZURE_RECOMMENDATION_TOOLTIP_IN_PROGRESS
				});

				if (this.migrationStateModel.performanceCollectionInProgress()) {
					await this._skuDataCollectionStatusIcon.updateProperties({
						iconPath: IconPathHelper.inProgressMigration
					});
					this._skuDataCollectionStatusText.value = this.hasRecommendations()
						? constants.AZURE_RECOMMENDATION_STATUS_REFINING
						: constants.AZURE_RECOMMENDATION_STATUS_IN_PROGRESS;

					if (await this.migrationStateModel.isWaitingForFirstTimeRefresh()) {
						const elapsedTimeInMins = Math.abs(new Date().getTime() - new Date(this.migrationStateModel._perfDataCollectionStartDate!).getTime()) / 60000;
						const skuRecAutoRefreshTimeInMins = this.migrationStateModel.refreshGetSkuRecommendationFrequency / 60000;

						this._skuDataCollectionTimerText.value = constants.AZURE_RECOMMENDATION_STATUS_AUTO_REFRESH_TIMER(Math.ceil(skuRecAutoRefreshTimeInMins - elapsedTimeInMins));
					} else {
						this._skuDataCollectionTimerText.value = constants.AZURE_RECOMMENDATION_STATUS_MANUAL_REFRESH_TIMER;
					}

					await this._skuGetRecommendationContainer.updateCssStyles({ 'display': 'none' });
					await this._skuDataCollectionStatusContainer.updateCssStyles({ 'display': 'block' });
					await this._skuStopDataCollectionButton.updateCssStyles({ 'display': 'block' });
					await this._skuRestartDataCollectionButton.updateCssStyles({ 'display': 'none' });
					await this._refreshAzureRecommendationButton.updateCssStyles({ 'display': 'block' });
					await this._skuEditParametersContainer.updateCssStyles({ 'display': 'block' });
				}

				else if (this.migrationStateModel.performanceCollectionStopped()) {
					await this._skuDataCollectionStatusIcon.updateProperties({
						iconPath: IconPathHelper.stop
					});
					this._skuDataCollectionStatusText.value = constants.AZURE_RECOMMENDATION_STATUS_STOPPED;
					this._skuDataCollectionTimerText.value = '';

					await this._skuGetRecommendationContainer.updateCssStyles({ 'display': 'none' });
					await this._skuDataCollectionStatusContainer.updateCssStyles({ 'display': 'block' });
					await this._skuStopDataCollectionButton.updateCssStyles({ 'display': 'none' });
					await this._skuRestartDataCollectionButton.updateCssStyles({ 'display': 'block' });
					await this._refreshAzureRecommendationButton.updateCssStyles({ 'display': 'none' });
					await this._skuEditParametersContainer.updateCssStyles({ 'display': 'block' });
				}
				break;
			}

			case PerformanceDataSourceOptions.OpenExisting: {
				await this._azureRecommendationSectionText.updateProperties({
					description: constants.AZURE_RECOMMENDATION_TOOLTIP_NOT_STARTED
				});

				if (this.hasRecommendations()) {
					this._azureRecommendationInfoText.value = constants.AZURE_RECOMMENDATION_STATUS_DATA_IMPORTED;
					this._getAzureRecommendationButton.label = constants.REFINE_AZURE_RECOMMENDATION;
					this._getAzureRecommendationButton.width = 200;

					await this._skuGetRecommendationContainer.updateCssStyles({ 'display': 'block' });
					await this._skuDataCollectionStatusContainer.updateCssStyles({ 'display': 'none' });
					await this._skuEditParametersContainer.updateCssStyles({ 'display': 'block' });
				}
				break;
			}

			// initial state before "Get Azure recommendation" dialog
			default: {
				await this._skuGetRecommendationContainer.updateCssStyles({ 'display': 'block' });
				await this._skuDataCollectionStatusContainer.updateCssStyles({ 'display': 'none' });
				await this._skuEditParametersContainer.updateCssStyles({ 'display': 'none' });
				await this._azureRecommendationSectionText.updateProperties({
					description: constants.AZURE_RECOMMENDATION_TOOLTIP_NOT_STARTED
				});
				break;
			}
		}

		await this.refreshCardText(false);
	}

	public hasRecommendations(): boolean {
		return this.migrationStateModel._skuRecommendationResults?.recommendations
			&& !this.migrationStateModel._skuRecommendationResults?.recommendationError
			? true
			: false;
	}
}

export enum CardDescriptionIndex {
	TARGET_TYPE = 0,
	ASSESSMENT_RESULTS_SECTION = 1,
	ASSESSMENT_STATUS = 2,
	ASSESSED_DBS = 3,
	RECOMMENDATION_RESULTS_SECTION = 4,
	SKU_RECOMMENDATION = 5,
	VM_CONFIGURATIONS = 6,
	VIEW_SKU_DETAILS = 7,
}
