/*---------------------------------------------------------------------------------------------
 *  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 { EOL } from 'os';
import { MigrationWizardPage } from '../models/migrationWizardPage';
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
import * as constants from '../constants/strings';
import * as styles from '../constants/styles';
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
import * as utils from '../api/utils';
import { MigrationTargetType } from '../api/utils';
import { azureResource } from 'azurecore';
import { AzureSqlDatabaseServer, getVMInstanceView, SqlVMServer } from '../api/azure';
import { collectTargetDatabaseInfo, TargetDatabaseInfo } from '../api/sqlUtils';
import { MigrationLocalStorage, MigrationServiceContext } from '../models/migrationLocalStorage';
import { ValidationErrorCodes } from '../constants/helper';
import { TdeMigrationDialog } from '../dialog/tdeConfiguration/tdeMigrationDialog';
import { TdeMigrationResult, TdeMigrationState } from '../models/tdeModels';

const TDE_MIGRATION_BUTTON_INDEX = 1;

export class TargetSelectionPage extends MigrationWizardPage {
	private _view!: azdata.ModelView;
	private _disposables: vscode.Disposable[] = [];

	private _pageDescription!: azdata.TextComponent;
	private _azureAccountsDropdown!: azdata.DropDownComponent;
	private _accountTenantDropdown!: azdata.DropDownComponent;
	private _accountTenantFlexContainer!: azdata.FlexContainer;
	private _azureSubscriptionDropdown!: azdata.DropDownComponent;
	private _azureLocationDropdown!: azdata.DropDownComponent;
	private _azureResourceGroupLabel!: azdata.TextComponent;
	private _azureResourceGroupDropdown!: azdata.DropDownComponent;
	private _azureResourceDropdownLabel!: azdata.TextComponent;
	private _azureResourceDropdown!: azdata.DropDownComponent;
	private _azureResourceTable!: azdata.DeclarativeTableComponent;
	private _resourceSelectionContainer!: azdata.FlexContainer;
	private _resourceAuthenticationContainer!: azdata.FlexContainer;
	private _targetUserNameInputBox!: azdata.InputBoxComponent;
	private _targetPasswordInputBox!: azdata.InputBoxComponent;
	private _testConectionButton!: azdata.ButtonComponent;
	private _connectionResultsInfoBox!: azdata.InfoBoxComponent;
	private _migrationTargetPlatform!: utils.MigrationTargetType;
	private _serviceContext!: MigrationServiceContext;
	private _certMigrationEventEmitter: vscode.EventEmitter<TdeMigrationResult>;
	private _certMigrationRequiredInfoBox!: azdata.InfoBoxComponent;

	constructor(
		wizard: azdata.window.Wizard,
		migrationStateModel: MigrationStateModel) {
		super(
			wizard,
			azdata.window.createWizardPage(constants.AZURE_SQL_TARGET_PAGE_TITLE),
			migrationStateModel);
		this._certMigrationEventEmitter = new vscode.EventEmitter();
		this._certMigrationEventEmitter.event(() => this._updateNextButton());
	}

	protected async registerContent(view: azdata.ModelView): Promise<void> {
		this._view = view;

		this._serviceContext = await MigrationLocalStorage.getMigrationServiceContext();

		this._pageDescription = this._view.modelBuilder.text()
			.withProps({
				value: constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(),
				CSSStyles: { ...styles.BODY_CSS, 'margin': '0' }
			}).component();

		this._certMigrationRequiredInfoBox = this._view.modelBuilder.infoBox()
			.withProps({
				text: constants.TDE_WIZARD_CERT_MIGRATION_BUTTON_MUST_BE_CLICKED,
				style: 'warning',
				CSSStyles: {
					...styles.BODY_CSS,
					'margin': '4px 14px 0px 14px',
					'text-align': 'justify',
					'width': WIZARD_INPUT_COMPONENT_WIDTH,
					'display': 'none'
				}
			}).component();

		const form = this._view.modelBuilder.formContainer()
			.withFormItems([
				{ component: this._pageDescription },
				{ component: this.createAzureAccountsDropdown() },
				{ component: this.createAzureTenantContainer() },
				{ component: this.createTargetDropdownContainer() },
				{ component: this._certMigrationRequiredInfoBox }
			]).withProps({
				CSSStyles: { 'padding-top': '0' }
			}).component();

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

		this._disposables.push(
			this.wizard.customButtons[TDE_MIGRATION_BUTTON_INDEX].onClick(
				async e => await this._startTdeMigration()));

		if (this.migrationStateModel.resumeAssessment) {
			await this.populateAzureAccountsDropdown();
		}

		await this._view.initializeModel(form);
	}

	public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
		this.wizard.customButtons[TDE_MIGRATION_BUTTON_INDEX].hidden = !this.migrationStateModel.tdeMigrationConfig.shouldAdsMigrateCertificates();
		this._updateNextButton();
		this._updateTdeMigrationButtonStatus();

		if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
			return;
		}
		switch (this.migrationStateModel._targetType) {
			case MigrationTargetType.SQLMI:
				this._pageDescription.value = constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(constants.SKU_RECOMMENDATION_MI_CARD_TEXT);
				this._azureResourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE;
				this._azureResourceDropdown.ariaLabel = constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE;
				break;
			case MigrationTargetType.SQLVM:
				this._pageDescription.value = constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(constants.SKU_RECOMMENDATION_VM_CARD_TEXT);
				this._azureResourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_VIRTUAL_MACHINE;
				this._azureResourceDropdown.ariaLabel = constants.AZURE_SQL_DATABASE_VIRTUAL_MACHINE;
				break;
			case MigrationTargetType.SQLDB:
				this._pageDescription.value = constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(constants.SKU_RECOMMENDATION_SQLDB_CARD_TEXT);
				this._azureResourceDropdownLabel.value = constants.AZURE_SQL_DATABASE;
				this._azureResourceDropdown.ariaLabel = constants.AZURE_SQL_DATABASE;
				this._updateConnectionButtonState();
				if (this.migrationStateModel._didUpdateDatabasesForMigration) {
					await this._resetTargetMapping();
					this.migrationStateModel._didUpdateDatabasesForMigration = false;
				}
				break;
		}

		this.wizard.registerNavigationValidator((pageChangeInfo) => {
			this.wizard.message = { text: '' };
			if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
				return true;
			}

			const errors: string[] = [];
			if (!this.migrationStateModel._azureAccount) {
				errors.push(constants.INVALID_ACCOUNT_ERROR);
			}

			if (!this.migrationStateModel._targetSubscription ||
				(<azdata.CategoryValue>this._azureSubscriptionDropdown.value)?.displayName === constants.NO_SUBSCRIPTIONS_FOUND) {
				errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
			}
			if (!this.migrationStateModel._location ||
				(<azdata.CategoryValue>this._azureLocationDropdown.value)?.displayName === constants.NO_LOCATION_FOUND) {
				errors.push(constants.INVALID_LOCATION_ERROR);
			}
			if (!this.migrationStateModel._resourceGroup ||
				(<azdata.CategoryValue>this._azureResourceGroupDropdown.value)?.displayName === constants.RESOURCE_GROUP_NOT_FOUND) {
				errors.push(constants.INVALID_RESOURCE_GROUP_ERROR);
			}

			const resourceDropdownValue = (<azdata.CategoryValue>this._azureResourceDropdown.value)?.displayName;
			switch (this.migrationStateModel._targetType) {
				case MigrationTargetType.SQLMI:
					const targetMi = this.migrationStateModel._targetServerInstance as azureResource.AzureSqlManagedInstance;
					if (!targetMi || resourceDropdownValue === constants.NO_MANAGED_INSTANCE_FOUND) {
						errors.push(constants.INVALID_MANAGED_INSTANCE_ERROR);
					}
					if (targetMi && targetMi.properties?.state.toLowerCase() !== 'Ready'.toLowerCase()) {
						errors.push(constants.MI_NOT_READY_ERROR(targetMi.name, targetMi.properties?.state));
					}
					if (this.migrationStateModel.tdeMigrationConfig.shouldAdsMigrateCertificates() &&
						this.migrationStateModel.tdeMigrationConfig.lastTdeMigrationResult().state !== TdeMigrationState.Succeeded) {
						errors.push(constants.TDE_MIGRATE_REQUIRED);
					}
					break;
				case MigrationTargetType.SQLVM:
					const targetVm = this.migrationStateModel._targetServerInstance as SqlVMServer;
					if (!targetVm || resourceDropdownValue === constants.NO_VIRTUAL_MACHINE_FOUND) {
						errors.push(constants.INVALID_VIRTUAL_MACHINE_ERROR);
					}

					// validate power state from VM instance view
					const vmInstanceView = this.migrationStateModel._vmInstanceView;
					if (!vmInstanceView.statuses.some(status => status.code.toLowerCase() === 'PowerState/running'.toLowerCase())) {
						errors.push(constants.VM_NOT_READY_POWER_STATE_ERROR(targetVm.name));
					}

					// validate IaaS extension mode
					if (targetVm.properties.sqlManagement.toLowerCase() !== 'Full'.toLowerCase()) {
						errors.push(constants.VM_NOT_READY_IAAS_EXTENSION_ERROR(targetVm.name, targetVm.properties.sqlManagement));
					}
					break;
				case MigrationTargetType.SQLDB:
					const targetSqlDB = this.migrationStateModel._targetServerInstance as AzureSqlDatabaseServer;
					if (!targetSqlDB || resourceDropdownValue === constants.NO_SQL_DATABASE_FOUND) {
						errors.push(constants.INVALID_SQL_DATABASE_ERROR);
					}
					// TODO: verify what state check is needed/possible?
					if (targetSqlDB && targetSqlDB.properties?.state.toLowerCase() !== 'Ready'.toLowerCase()) {
						errors.push(constants.SQLDB_NOT_READY_ERROR(targetSqlDB.name, targetSqlDB.properties.state));
					}

					// validate target sqldb username exists
					const targetUsernameValue = this._targetUserNameInputBox.value ?? '';
					if (targetUsernameValue.length < 1) {
						errors.push(constants.MISSING_TARGET_USERNAME_ERROR);
					}

					// validate target sqldb password exists
					const targetPasswordValue = this._targetPasswordInputBox.value ?? '';
					if (targetPasswordValue.length < 1) {
						errors.push(constants.MISSING_TARGET_PASSWORD_ERROR);
					}

					// validate source and target database mapping
					const mappingErrors = this._getSourceTargetMappingErrors();
					if (mappingErrors.length > 0) {
						errors.push(...mappingErrors);
					}
					break;
			}

			if (errors.length > 0) {
				this.wizard.message = {
					text: errors.join(EOL),
					level: azdata.window.MessageLevel.Error
				};
				return false;
			}

			return true;
		});

		if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
			return;
		}
		switch (this.migrationStateModel._targetType) {
			case MigrationTargetType.SQLMI:
				this._pageDescription.value = constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(constants.SKU_RECOMMENDATION_MI_CARD_TEXT);
				this._azureResourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE;
				this._azureResourceDropdown.ariaLabel = constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE;
				break;
			case MigrationTargetType.SQLVM:
				this._pageDescription.value = constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(constants.SKU_RECOMMENDATION_VM_CARD_TEXT);
				this._azureResourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_VIRTUAL_MACHINE;
				this._azureResourceDropdown.ariaLabel = constants.AZURE_SQL_DATABASE_VIRTUAL_MACHINE;
				break;
			case MigrationTargetType.SQLDB:
				this._pageDescription.value = constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(constants.SKU_RECOMMENDATION_SQLDB_CARD_TEXT);
				this._azureResourceDropdownLabel.value = constants.AZURE_SQL_DATABASE;
				this._azureResourceDropdown.ariaLabel = constants.AZURE_SQL_DATABASE;
				this._updateConnectionButtonState();
				if (this.migrationStateModel._didUpdateDatabasesForMigration) {
					await this._resetTargetMapping();
					this.migrationStateModel._didUpdateDatabasesForMigration = false;
				}
				break;
		}

		const isSqlDbTarget = this.migrationStateModel._targetType === MigrationTargetType.SQLDB;
		await this._targetUserNameInputBox.updateProperties({ required: isSqlDbTarget });
		await this._targetPasswordInputBox.updateProperties({ required: isSqlDbTarget });
		await utils.updateControlDisplay(this._resourceAuthenticationContainer, isSqlDbTarget);

		if (this._migrationTargetPlatform !== this.migrationStateModel._targetType) {
			// if the user had previously selected values on this page, then went back to change the migration target platform
			// and came back, forcibly reload the location/resource group/resource values since they will now be different
			this._migrationTargetPlatform = this.migrationStateModel._targetType;

			this._targetPasswordInputBox.value = '';
			this.migrationStateModel._sqlMigrationServices = undefined!;
			this.migrationStateModel._azureAccount = undefined!;
			this.migrationStateModel._azureTenant = undefined!;
			this.migrationStateModel._targetSubscription = undefined!;
			this.migrationStateModel._location = undefined!;
			this.migrationStateModel._resourceGroup = undefined!;
			this.migrationStateModel._targetServerInstance = undefined!;

			await utils.clearDropDown(this._azureAccountsDropdown);
			await utils.clearDropDown(this._accountTenantDropdown);
			await utils.clearDropDown(this._azureSubscriptionDropdown);
			await utils.clearDropDown(this._azureLocationDropdown);
			await utils.clearDropDown(this._azureResourceGroupDropdown);
			await utils.clearDropDown(this._azureResourceDropdown);
		}

		await utils.updateControlDisplay(this._certMigrationRequiredInfoBox, this.migrationStateModel.tdeMigrationConfig.shouldAdsMigrateCertificates());
		await this.populateAzureAccountsDropdown();
	}

	public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
		this.wizard.registerNavigationValidator(pageChangeInfo => true);
		this.wizard.message = { text: '' };
		this.wizard.customButtons[TDE_MIGRATION_BUTTON_INDEX].hidden = true;
	}

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

	private createAzureAccountsDropdown(): azdata.FlexContainer {
		const azureAccountLabel = this._view.modelBuilder.text()
			.withProps({
				value: constants.ACCOUNTS_SELECTION_PAGE_TITLE,
				width: WIZARD_INPUT_COMPONENT_WIDTH,
				requiredIndicator: true,
				CSSStyles: { ...styles.LABEL_CSS, 'margin-top': '-1em' }
			}).component();
		this._azureAccountsDropdown = this._view.modelBuilder.dropDown()
			.withProps({
				ariaLabel: constants.ACCOUNTS_SELECTION_PAGE_TITLE,
				width: WIZARD_INPUT_COMPONENT_WIDTH,
				editable: true,
				required: true,
				fireOnTextChange: true,
				placeholder: constants.SELECT_AN_ACCOUNT,
				CSSStyles: { 'margin-top': '-1em' },
			}).component();
		this._disposables.push(
			this._azureAccountsDropdown.onValueChanged(async (value) => {
				if (value && value !== 'undefined') {
					const selectedAccount = this.migrationStateModel._azureAccounts?.find(account => account.displayInfo.displayName === value);
					this.migrationStateModel._azureAccount = (selectedAccount)
						? utils.deepClone(selectedAccount)!
						: undefined!;
				} else {
					this.migrationStateModel._azureAccount = undefined!;
				}
				await utils.clearDropDown(this._accountTenantDropdown);
				await this.populateTenantsDropdown();
			}));

		const linkAccountButton = this._view.modelBuilder.hyperlink()
			.withProps({
				label: constants.ACCOUNT_LINK_BUTTON_LABEL,
				url: '',
				CSSStyles: { ...styles.BODY_CSS }
			})
			.component();

		this._disposables.push(
			linkAccountButton.onDidClick(async (event) => {
				await vscode.commands.executeCommand('workbench.actions.modal.linkedAccount');
				await this.populateAzureAccountsDropdown();
				this.wizard.message = { text: '' };
				await this._azureAccountsDropdown.validate();
			}));

		return this._view.modelBuilder.flexContainer()
			.withLayout({ flexFlow: 'column' })
			.withItems([
				azureAccountLabel,
				this._azureAccountsDropdown,
				linkAccountButton])
			.component();
	}

	private createAzureTenantContainer(): azdata.FlexContainer {
		const azureTenantDropdownLabel = this._view.modelBuilder.text()
			.withProps({
				value: constants.AZURE_TENANT,
				CSSStyles: { ...styles.LABEL_CSS }
			}).component();

		this._accountTenantDropdown = this._view.modelBuilder.dropDown()
			.withProps({
				ariaLabel: constants.AZURE_TENANT,
				width: WIZARD_INPUT_COMPONENT_WIDTH,
				editable: true,
				fireOnTextChange: true,
				placeholder: constants.SELECT_A_TENANT
			}).component();

		this._disposables.push(
			this._accountTenantDropdown.onValueChanged(async (value) => {
				if (value && value !== 'undefined') {
					const selectedTenant = this.migrationStateModel._accountTenants?.find(tenant => tenant.displayName === value);
					this.migrationStateModel._azureTenant = selectedTenant
						? utils.deepClone(selectedTenant)
						: undefined!;
				} else {
					this.migrationStateModel._azureTenant = undefined!;
				}
				await utils.clearDropDown(this._azureSubscriptionDropdown);
				await this.populateSubscriptionDropdown();
			}));

		this._accountTenantFlexContainer = this._view.modelBuilder.flexContainer()
			.withLayout({ flexFlow: 'column' })
			.withItems([
				azureTenantDropdownLabel,
				this._accountTenantDropdown])
			.withProps({ CSSStyles: { 'display': 'none' } })
			.component();
		return this._accountTenantFlexContainer;
	}

	private createTargetDropdownContainer(): azdata.FlexContainer {
		const subscriptionDropdownLabel = this._view.modelBuilder.text()
			.withProps({
				value: constants.SUBSCRIPTION,
				description: constants.TARGET_SUBSCRIPTION_INFO,
				width: WIZARD_INPUT_COMPONENT_WIDTH,
				requiredIndicator: true,
				CSSStyles: { ...styles.LABEL_CSS, }
			}).component();
		this._azureSubscriptionDropdown = this._view.modelBuilder.dropDown()
			.withProps({
				ariaLabel: constants.SUBSCRIPTION,
				width: WIZARD_INPUT_COMPONENT_WIDTH,
				editable: true,
				required: true,
				fireOnTextChange: true,
				placeholder: constants.SELECT_A_SUBSCRIPTION,
				CSSStyles: { 'margin-top': '-1em' },
			}).component();
		this._disposables.push(
			this._azureSubscriptionDropdown.onValueChanged(async (value) => {
				if (value && value !== 'undefined' && value !== constants.NO_SUBSCRIPTIONS_FOUND) {
					const selectedSubscription = this.migrationStateModel._subscriptions?.find(
						subscription => `${subscription.name} - ${subscription.id}` === value);
					this.migrationStateModel._targetSubscription = (selectedSubscription)
						? utils.deepClone(selectedSubscription)!
						: undefined!;
				} else {
					this.migrationStateModel._targetSubscription = undefined!;
				}
				this.migrationStateModel.refreshDatabaseBackupPage = true;
				await utils.clearDropDown(this._azureLocationDropdown);
				await this.populateLocationDropdown();
			}));

		const azureLocationLabel = this._view.modelBuilder.text()
			.withProps({
				value: constants.LOCATION,
				description: constants.TARGET_LOCATION_INFO,
				width: WIZARD_INPUT_COMPONENT_WIDTH,
				requiredIndicator: true,
				CSSStyles: { ...styles.LABEL_CSS }
			}).component();
		this._azureLocationDropdown = this._view.modelBuilder.dropDown()
			.withProps({
				ariaLabel: constants.LOCATION,
				width: WIZARD_INPUT_COMPONENT_WIDTH,
				editable: true,
				required: true,
				fireOnTextChange: true,
				placeholder: constants.SELECT_A_LOCATION,
				CSSStyles: { 'margin-top': '-1em' },
			}).component();
		this._disposables.push(
			this._azureLocationDropdown.onValueChanged(async (value) => {
				if (value && value !== 'undefined' && value !== constants.NO_LOCATION_FOUND) {
					const selectedLocation = this.migrationStateModel._locations?.find(location => location.displayName === value);
					this.migrationStateModel._location = (selectedLocation)
						? utils.deepClone(selectedLocation)!
						: undefined!;
				} else {
					this.migrationStateModel._location = undefined!;
				}
				this.migrationStateModel.refreshDatabaseBackupPage = true;
				await utils.clearDropDown(this._azureResourceGroupDropdown);
				await this.populateResourceGroupDropdown();
			}));

		this._resourceSelectionContainer = this._createResourceDropdowns();
		this._resourceAuthenticationContainer = this._createResourceAuthenticationContainer();

		return this._view.modelBuilder.flexContainer()
			.withItems([
				subscriptionDropdownLabel,
				this._azureSubscriptionDropdown,
				azureLocationLabel,
				this._azureLocationDropdown,
				this._resourceSelectionContainer,
				this._resourceAuthenticationContainer])
			.withLayout({ flexFlow: 'column' })
			.component();
	}

	private _createResourceAuthenticationContainer(): azdata.FlexContainer {
		// target user name
		const targetUserNameLabel = this._view.modelBuilder.text()
			.withProps({
				value: constants.TARGET_USERNAME_LAbEL,
				requiredIndicator: true,
				CSSStyles: { ...styles.LABEL_CSS, 'margin-top': '-1em' }
			}).component();
		this._targetUserNameInputBox = this._view.modelBuilder.inputBox()
			.withProps({
				width: '300px',
				inputType: 'text',
				placeHolder: constants.TARGET_USERNAME_PLACEHOLDER,
				required: false,
				CSSStyles: { 'margin-top': '-1em' },
			}).component();

		this._disposables.push(
			this._targetUserNameInputBox.onTextChanged(
				async (value: string) => {
					this.migrationStateModel._targetUserName = value ?? '';
					await this._resetTargetMapping();
				}));

		// target password
		const targetPasswordLabel = this._view.modelBuilder.text()
			.withProps({
				value: constants.TARGET_PASSWORD_LAbEL,
				requiredIndicator: true,
				title: '',
				CSSStyles: { ...styles.LABEL_CSS, 'margin-top': '-1em' }
			}).component();
		this._targetPasswordInputBox = this._view.modelBuilder.inputBox()
			.withProps({
				width: '300px',
				inputType: 'password',
				placeHolder: constants.TARGET_PASSWORD_PLACEHOLDER,
				required: false,
				CSSStyles: { 'margin-top': '-1em' },
			}).component();
		this._disposables.push(
			this._targetPasswordInputBox.onTextChanged(
				async (value: string) => {
					this.migrationStateModel._targetPassword = value ?? '';
					await this._resetTargetMapping();
				}));

		// test connection button
		this._testConectionButton = this._view.modelBuilder.button()
			.withProps({
				enabled: false,
				label: constants.TARGET_CONNECTION_LABEL,
				width: '80px',
			}).component();

		this._connectionResultsInfoBox = this._view.modelBuilder.infoBox()
			.withProps({
				style: 'success',
				text: '',
				announceText: true,
				CSSStyles: { 'display': 'none' },
			})
			.component();

		const connectionButtonLoadingContainer = this._view.modelBuilder.loadingComponent()
			.withItem(this._testConectionButton)
			.withProps({ loading: false })
			.component();

		this._disposables.push(
			this._testConectionButton.onDidClick(async (value) => {
				this.wizard.message = { text: '' };

				const targetDatabaseServer = this.migrationStateModel._targetServerInstance as AzureSqlDatabaseServer;
				const userName = this.migrationStateModel._targetUserName;
				const password = this.migrationStateModel._targetPassword;
				const targetDatabases: TargetDatabaseInfo[] = [];
				if (targetDatabaseServer && userName && password) {
					try {
						connectionButtonLoadingContainer.loading = true;
						await utils.updateControlDisplay(this._connectionResultsInfoBox, false);
						targetDatabases.push(
							...await collectTargetDatabaseInfo(
								targetDatabaseServer,
								userName,
								password));
						await this._showConnectionResults(targetDatabases);
					} catch (error) {
						this.wizard.message = {
							level: azdata.window.MessageLevel.Error,
							text: constants.AZURE_SQL_TARGET_CONNECTION_ERROR_TITLE,
							description: constants.SQL_TARGET_CONNECTION_ERROR(error.message),
						};
						await this._showConnectionResults(
							targetDatabases,
							constants.AZURE_SQL_TARGET_CONNECTION_ERROR_TITLE);
					}
					finally {
						connectionButtonLoadingContainer.loading = false;
					}
				}
				await this._populateResourceMappingTable(targetDatabases);
			}));

		const connectionContainer = this._view.modelBuilder.flexContainer()
			.withItems([
				connectionButtonLoadingContainer,
				this._connectionResultsInfoBox],
				{ flex: '0 0 auto' })
			.withLayout({
				flexFlow: 'row',
				alignContent: 'center',
				alignItems: 'center',
			})
			.withProps({ CSSStyles: { 'margin': '15px 0 0 0' } })
			.component();

		const mapSourceHeading = this._view.modelBuilder.text()
			.withProps({
				value: constants.MAP_SOURCE_TARGET_HEADING,
				width: WIZARD_INPUT_COMPONENT_WIDTH,
				CSSStyles: {
					'font-weight': '600',
					'font-size': '16px',
					'margin': '15px 0 0 0',
				},
			})
			.component();
		const mapSourceDetails = this._view.modelBuilder.text()
			.withProps({
				value: constants.MAP_SOURCE_TARGET_DESCRIPTION,
				width: WIZARD_INPUT_COMPONENT_WIDTH,
				CSSStyles: {
					'font-size': '13px',
					'margin': '15px 0 15px 0',
				},
			})
			.component();
		this._azureResourceTable = this._createResourceTable();

		return this._view.modelBuilder.flexContainer()
			.withItems([
				targetUserNameLabel,
				this._targetUserNameInputBox,
				targetPasswordLabel,
				this._targetPasswordInputBox,
				connectionContainer,
				mapSourceHeading,
				mapSourceDetails,
				this._azureResourceTable])
			.withLayout({ flexFlow: 'column' })
			.withProps({ CSSStyles: { 'margin': '15px 0 0 0' } })
			.component();
	}

	private async _resetTargetMapping(): Promise<void> {
		this._initializeSourceTargetDatabaseMap();
		this._updateConnectionButtonState();
		await this._azureResourceTable.setDataValues([]);
		await utils.updateControlDisplay(this._connectionResultsInfoBox, false);
	}

	private async _showConnectionResults(
		databases: TargetDatabaseInfo[],
		errorMessage?: string): Promise<void> {

		const hasError = errorMessage !== undefined;
		const hasDatabases = databases.length > 0;
		this._connectionResultsInfoBox.style = hasError
			? 'error'
			: hasDatabases
				? 'success'
				: 'warning';
		this._connectionResultsInfoBox.text = hasError
			? constants.SQL_TARGET_CONNECTION_ERROR(errorMessage)
			: hasDatabases
				? constants.SQL_TARGET_CONNECTION_SUCCESS(databases.length.toLocaleString())
				: constants.SQL_TARGET_MISSING_SOURCE_DATABASES;
		await utils.updateControlDisplay(this._connectionResultsInfoBox, true);
	}

	private _createResourceDropdowns(): azdata.FlexContainer {
		this._azureResourceGroupLabel = this._view.modelBuilder.text()
			.withProps({
				value: constants.RESOURCE_GROUP,
				description: constants.TARGET_RESOURCE_GROUP_INFO,
				width: WIZARD_INPUT_COMPONENT_WIDTH,
				requiredIndicator: true,
				CSSStyles: { ...styles.LABEL_CSS }
			}).component();
		this._azureResourceGroupDropdown = this._view.modelBuilder.dropDown()
			.withProps({
				ariaLabel: constants.RESOURCE_GROUP,
				width: WIZARD_INPUT_COMPONENT_WIDTH,
				editable: true,
				required: true,
				fireOnTextChange: true,
				placeholder: constants.SELECT_A_RESOURCE_GROUP,
				CSSStyles: { 'margin-top': '-1em' },
			}).component();
		this._disposables.push(
			this._azureResourceGroupDropdown.onValueChanged(async (value) => {
				if (value && value !== 'undefined' && value !== constants.RESOURCE_GROUP_NOT_FOUND) {
					const selectedResourceGroup = this.migrationStateModel._resourceGroups?.find(rg => rg.name === value);
					this.migrationStateModel._resourceGroup = (selectedResourceGroup)
						? utils.deepClone(selectedResourceGroup)!
						: undefined!;
				} else {
					this.migrationStateModel._resourceGroup = undefined!;
				}
				await utils.clearDropDown(this._azureResourceDropdown);
				await this.populateResourceInstanceDropdown();
			}));

		this._azureResourceDropdownLabel = this._view.modelBuilder.text()
			.withProps({
				value: constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE,
				description: constants.TARGET_RESOURCE_INFO,
				width: WIZARD_INPUT_COMPONENT_WIDTH,
				requiredIndicator: true,
				CSSStyles: { ...styles.LABEL_CSS }
			}).component();
		this._azureResourceDropdown = this._view.modelBuilder.dropDown()
			.withProps({
				ariaLabel: constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE,
				width: WIZARD_INPUT_COMPONENT_WIDTH,
				editable: true,
				required: true,
				fireOnTextChange: true,
				placeholder: constants.SELECT_SERVICE_PLACEHOLDER,
				CSSStyles: { 'margin-top': '-1em' },
				loading: false,
			}).component();
		this._disposables.push(
			this._azureResourceDropdown.onValueChanged(async (value) => {
				const isSqlDbTarget = this.migrationStateModel._targetType === MigrationTargetType.SQLDB;
				if (value && value !== 'undefined' &&
					value !== constants.NO_MANAGED_INSTANCE_FOUND &&
					value !== constants.NO_SQL_DATABASE_SERVER_FOUND &&
					value !== constants.NO_VIRTUAL_MACHINE_FOUND) {

					switch (this.migrationStateModel._targetType) {
						case MigrationTargetType.SQLVM:
							const selectedVm = this.migrationStateModel._targetSqlVirtualMachines?.find(vm => vm.name === value
								|| constants.UNAVAILABLE_TARGET_PREFIX(vm.name) === value);

							if (selectedVm) {
								this.migrationStateModel._targetServerInstance = utils.deepClone(selectedVm)! as SqlVMServer;
								this.migrationStateModel._vmInstanceView = await getVMInstanceView(
									this.migrationStateModel._targetServerInstance,
									this.migrationStateModel._azureAccount,
									this.migrationStateModel._targetSubscription);
								this.wizard.message = { text: '' };

								// validate power state from VM instance view
								if (!this.migrationStateModel._vmInstanceView.statuses.some(status => status.code.toLowerCase() === 'PowerState/running'.toLowerCase())) {
									this.wizard.message = {
										text: constants.VM_NOT_READY_POWER_STATE_ERROR(this.migrationStateModel._targetServerInstance.name),
										level: azdata.window.MessageLevel.Error
									};
								}

								// validate IaaS extension mode
								if (this.migrationStateModel._targetServerInstance.properties.sqlManagement.toLowerCase() !== 'Full'.toLowerCase()) {
									this.wizard.message = {
										text: constants.VM_NOT_READY_IAAS_EXTENSION_ERROR(this.migrationStateModel._targetServerInstance.name, this.migrationStateModel._targetServerInstance.properties.sqlManagement),
										level: azdata.window.MessageLevel.Error
									};
								}
							}
							break;
						case MigrationTargetType.SQLMI:
							const selectedMi = this.migrationStateModel._targetManagedInstances?.find(
								mi => mi.name === value ||
									constants.UNAVAILABLE_TARGET_PREFIX(mi.name) === value);

							if (selectedMi) {
								this.migrationStateModel._targetServerInstance = utils.deepClone(selectedMi)! as azureResource.AzureSqlManagedInstance;
								this.wizard.message = { text: '' };

								if (this.migrationStateModel._targetServerInstance.properties.state.toLowerCase() !== 'Ready'.toLowerCase()) {
									this.wizard.message = {
										text: constants.MI_NOT_READY_ERROR(
											this.migrationStateModel._targetServerInstance.name,
											this.migrationStateModel._targetServerInstance.properties.state),
										level: azdata.window.MessageLevel.Error
									};
								}
							}

							this.migrationStateModel.tdeMigrationConfig.resetTdeMigrationResult();
							break;
						case MigrationTargetType.SQLDB:
							const sqlDatabaseServer = this.migrationStateModel._targetSqlDatabaseServers?.find(
								sqldb => sqldb.name === value || constants.UNAVAILABLE_TARGET_PREFIX(sqldb.name) === value);

							if (sqlDatabaseServer) {
								this.migrationStateModel._targetServerInstance = utils.deepClone(sqlDatabaseServer)! as AzureSqlDatabaseServer;
								this.wizard.message = { text: '' };
								if (this.migrationStateModel._targetServerInstance.properties.state.toLowerCase() === 'Ready'.toLowerCase()) {
									this._targetUserNameInputBox.value = this.migrationStateModel._targetServerInstance.properties.administratorLogin;
								} else {
									this.wizard.message = {
										text: constants.SQLDB_NOT_READY_ERROR(
											this.migrationStateModel._targetServerInstance.name,
											this.migrationStateModel._targetServerInstance.properties.state),
										level: azdata.window.MessageLevel.Error
									};
								}
							}
							break;
					}

					await this._validateFields();
				} else {
					this.migrationStateModel._targetServerInstance = undefined!;
					this.migrationStateModel._vmInstanceView = undefined!;

					if (isSqlDbTarget) {
						this._targetUserNameInputBox.value = '';
					}
				}

				this._resetTdeMigrationResult();

				this.migrationStateModel._sqlMigrationServices = undefined!;
				if (isSqlDbTarget) {
					await this._resetTargetMapping();
					this._targetPasswordInputBox.value = '';
				}
			}));

		return this._view.modelBuilder.flexContainer()
			.withItems([
				this._azureResourceGroupLabel,
				this._azureResourceGroupDropdown,
				this._azureResourceDropdownLabel,
				this._azureResourceDropdown])
			.withLayout({ flexFlow: 'column' })
			.component();
	}

	private _initializeSourceTargetDatabaseMap(): void {
		// initialize source / target database map
		this.migrationStateModel._sourceTargetMapping = new Map();
		this.migrationStateModel._targetDatabaseNames = [];
		this.migrationStateModel._databasesForMigration.forEach(
			sourceDatabaseName => this.migrationStateModel._sourceTargetMapping.set(
				sourceDatabaseName, undefined));
	}

	private _updateConnectionButtonState(): void {
		const targetDatabaseServer = (this._azureResourceDropdown.value as azdata.CategoryValue)?.name ?? '';
		const userName = this.migrationStateModel._targetUserName ?? '';
		const password = this.migrationStateModel._targetPassword ?? '';
		this._testConectionButton.enabled = targetDatabaseServer.length > 0
			&& userName.length > 0
			&& password.length > 0;
	}

	private _createResourceTable(): azdata.DeclarativeTableComponent {
		const columWidth = '50%';
		const headerStyles = {
			'padding': '0px',
			'border-style': 'none',
			'text-align': 'left',
			'font-weight': '600',
		};
		const rowStyles = {
			'padding': '1px 0px',
			'border-style': 'none',
			'text-align': 'left',
			'white-space': 'nowrap',
			'text-overflow': 'ellipsis',
			'overflow': 'hidden',
		};

		return this._view.modelBuilder.declarativeTable()
			.withProps({
				ariaLabel: constants.MAP_SOURCE_TARGET_HEADING,
				width: WIZARD_INPUT_COMPONENT_WIDTH,
				data: [],
				display: 'inline-block',
				CSSStyles: { 'padding': '0px' },
				columns: [
					{
						displayName: constants.MAP_SOURCE_COLUMN,
						valueType: azdata.DeclarativeDataType.string,
						width: columWidth,
						isReadOnly: true,
						rowCssStyles: rowStyles,
						headerCssStyles: headerStyles,
					},
					{
						displayName: constants.MAP_TARGET_COLUMN,
						valueType: azdata.DeclarativeDataType.component,
						width: columWidth,
						isReadOnly: false,
						rowCssStyles: rowStyles,
						headerCssStyles: headerStyles,
					},
				],
			})
			.withValidation(
				table =>
					this.migrationStateModel._targetType !== MigrationTargetType.SQLDB
					|| (table.dataValues !== undefined && table.dataValues.length > 0))
			.component();
	}

	private async populateAzureAccountsDropdown(): Promise<void> {
		try {
			this._azureAccountsDropdown.loading = true;
			this.migrationStateModel._azureAccounts = await utils.getAzureAccounts();

			const accountId =
				this.migrationStateModel._azureAccount?.displayInfo?.userId ??
				this._serviceContext?.azureAccount?.displayInfo?.userId;
			this._azureAccountsDropdown.values = await utils.getAzureAccountsDropdownValues(this.migrationStateModel._azureAccounts);

			utils.selectDefaultDropdownValue(
				this._azureAccountsDropdown,
				accountId,
				false);
		} finally {
			this._azureAccountsDropdown.loading = false;
		}
	}

	private async populateTenantsDropdown(): Promise<void> {
		try {
			this._accountTenantDropdown.loading = true;

			if (!utils.isAccountTokenStale(this.migrationStateModel._azureAccount) &&
				this.migrationStateModel._azureAccount?.properties?.tenants?.length > 0) {
				this.migrationStateModel._accountTenants = utils.getAzureTenants(
					this.migrationStateModel._azureAccount);

				const tenantId = this.migrationStateModel._azureTenant?.id;

				this._accountTenantDropdown.values = utils.getAzureTenantsDropdownValues(this.migrationStateModel._accountTenants);

				utils.selectDefaultDropdownValue(
					this._accountTenantDropdown,
					tenantId,
					true);
			}
			await this._azureAccountsDropdown.validate();
		} finally {
			this._accountTenantDropdown.loading = false;
			await this._accountTenantFlexContainer.updateCssStyles(
				this.migrationStateModel._azureAccount?.properties?.tenants?.length > 1
					? { 'display': 'inline' }
					: { 'display': 'none' }
			);
		}
	}

	private async populateSubscriptionDropdown(): Promise<void> {
		try {
			this._azureSubscriptionDropdown.loading = true;
			this.migrationStateModel._subscriptions = await utils.getAzureSubscriptions(
				this.migrationStateModel._azureAccount,
				this.migrationStateModel._azureTenant?.id);
			const subscriptionId =
				this.migrationStateModel._targetSubscription?.id ??
				this._serviceContext?.subscription?.id;

			this._azureSubscriptionDropdown.values = await utils.getAzureSubscriptionsDropdownValues(this.migrationStateModel._subscriptions);

			utils.selectDefaultDropdownValue(
				this._azureSubscriptionDropdown,
				subscriptionId,
				false);
		} catch (e) {
			console.log(e);
		} finally {
			this._azureSubscriptionDropdown.loading = false;
		}
	}

	public async populateLocationDropdown(): Promise<void> {
		try {
			this._azureLocationDropdown.loading = true;
			switch (this.migrationStateModel._targetType) {
				case MigrationTargetType.SQLMI:
					this.migrationStateModel._targetManagedInstances = await utils.getManagedInstances(
						this.migrationStateModel._azureAccount,
						this.migrationStateModel._targetSubscription);
					this.migrationStateModel._locations = await utils.getResourceLocations(
						this.migrationStateModel._azureAccount,
						this.migrationStateModel._targetSubscription,
						this.migrationStateModel._targetManagedInstances);
					break;
				case MigrationTargetType.SQLVM:
					this.migrationStateModel._targetSqlVirtualMachines = await utils.getVirtualMachines(
						this.migrationStateModel._azureAccount,
						this.migrationStateModel._targetSubscription);
					this.migrationStateModel._locations = await utils.getResourceLocations(
						this.migrationStateModel._azureAccount,
						this.migrationStateModel._targetSubscription,
						this.migrationStateModel._targetSqlVirtualMachines);
					break;
				case MigrationTargetType.SQLDB:
					this.migrationStateModel._targetSqlDatabaseServers = await utils.getAzureSqlDatabaseServers(
						this.migrationStateModel._azureAccount,
						this.migrationStateModel._targetSubscription);
					this.migrationStateModel._locations = await utils.getResourceLocations(
						this.migrationStateModel._azureAccount,
						this.migrationStateModel._targetSubscription,
						this.migrationStateModel._targetSqlDatabaseServers);
					break;
			}

			const location =
				this.migrationStateModel._location?.displayName ??
				this._serviceContext?.location?.displayName;

			this._azureLocationDropdown.values = utils.getAzureLocationsDropdownValues(this.migrationStateModel._locations);
			utils.selectDefaultDropdownValue(
				this._azureLocationDropdown,
				location,
				true);
		} catch (e) {
			console.log(e);
		} finally {
			this._azureLocationDropdown.loading = false;
		}
	}

	public async populateResourceGroupDropdown(): Promise<void> {
		try {
			this._azureResourceGroupDropdown.loading = true;
			switch (this.migrationStateModel._targetType) {
				case MigrationTargetType.SQLMI:
					this.migrationStateModel._resourceGroups = utils.getServiceResourceGroupsByLocation(
						this.migrationStateModel._targetManagedInstances,
						this.migrationStateModel._location);
					break;
				case MigrationTargetType.SQLVM:
					this.migrationStateModel._resourceGroups = utils.getServiceResourceGroupsByLocation(
						this.migrationStateModel._targetSqlVirtualMachines,
						this.migrationStateModel._location);
					break;
				case MigrationTargetType.SQLDB:
					this.migrationStateModel._resourceGroups = utils.getServiceResourceGroupsByLocation(
						this.migrationStateModel._targetSqlDatabaseServers,
						this.migrationStateModel._location);
					break;
			}

			const resourceGroupId = this.migrationStateModel._resourceGroup?.id;

			this._azureResourceGroupDropdown.values = utils.getResourceDropdownValues(
				this.migrationStateModel._resourceGroups,
				constants.RESOURCE_GROUP_NOT_FOUND);

			utils.selectDefaultDropdownValue(
				this._azureResourceGroupDropdown,
				resourceGroupId,
				false);
		} catch (e) {
			console.log(e);
		} finally {
			this._azureResourceGroupDropdown.loading = false;
		}
	}

	private async populateResourceInstanceDropdown(): Promise<void> {
		try {
			this._azureResourceDropdown.loading = true;
			const targetName = this.migrationStateModel.migrationTargetServerName;

			switch (this.migrationStateModel._targetType) {
				case MigrationTargetType.SQLMI:
					this._azureResourceDropdown.values = await utils.getManagedInstancesDropdownValues(
						this.migrationStateModel._targetManagedInstances,
						this.migrationStateModel._location,
						this.migrationStateModel._resourceGroup);
					this._updateTdeMigrationButtonStatus();
					break;
				case MigrationTargetType.SQLVM:
					this._azureResourceDropdown.values = await utils.getVirtualMachinesDropdownValues(
						this.migrationStateModel._targetSqlVirtualMachines,
						this.migrationStateModel._location,
						this.migrationStateModel._resourceGroup,
						this.migrationStateModel._azureAccount,
						this.migrationStateModel._targetSubscription);
					break;
				case MigrationTargetType.SQLDB:
					this._azureResourceDropdown.values = utils.getAzureResourceDropdownValues(
						this.migrationStateModel._targetSqlDatabaseServers,
						this.migrationStateModel._location,
						this.migrationStateModel._resourceGroup?.name,
						constants.NO_SQL_DATABASE_SERVER_FOUND);
					break;
			}

			utils.selectDefaultDropdownValue(
				this._azureResourceDropdown,
				targetName,
				true);
		} finally {
			this._azureResourceDropdown.loading = false;
		}
	}

	private async _populateResourceMappingTable(targetDatabases: TargetDatabaseInfo[]): Promise<void> {
		// populate target database list
		const databaseValues = this._getTargetDatabaseDropdownValues(
			targetDatabases,
			constants.NO_SQL_DATABASE_FOUND);

		const data: azdata.DeclarativeTableCellValue[][] = this.migrationStateModel._databasesForMigration
			.map(sourceDatabase => {
				// target database dropdown
				const targetDatabaseDropDown = this._view.modelBuilder.dropDown()
					.withProps({
						width: '100%',
						required: true,
						editable: true,
						fireOnTextChange: true,
						values: databaseValues,
						placeholder: constants.MAP_TARGET_PLACEHOLDER,
					})
					.component();
				this._disposables.push(
					targetDatabaseDropDown.onValueChanged((targetDatabaseName: string) => {
						const targetDatabaseInfo = targetDatabases?.find(
							targetDb => targetDb.databaseName === targetDatabaseName);
						this.migrationStateModel._sourceTargetMapping.set(
							sourceDatabase,
							targetDatabaseInfo);
						this.migrationStateModel.refreshDatabaseBackupPage = true;
						this.migrationStateModel._didDatabaseMappingChange = true;
					}));

				const targetDatabaseName = this.migrationStateModel._sourceTargetMapping.get(sourceDatabase)?.databaseName ?? '';
				if (targetDatabaseName.length > 0) {
					utils.selectDefaultDropdownValue(
						targetDatabaseDropDown,
						targetDatabaseName);
				}

				return [
					<azdata.DeclarativeTableCellValue>{ value: sourceDatabase },
					<azdata.DeclarativeTableCellValue>{ value: targetDatabaseDropDown },
				];
			}) || [];

		await this._azureResourceTable.setDataValues(data);
	}

	private _getTargetDatabaseDropdownValues(
		databases: TargetDatabaseInfo[],
		resourceNotFoundMessage: string): azdata.CategoryValue[] {

		if (databases?.length > 0) {
			return databases.map<azdata.CategoryValue>(database => {
				const databaseName = database.databaseName;
				return { name: databaseName, displayName: databaseName };
			});
		}

		return [{ name: '', displayName: resourceNotFoundMessage }];
	}

	private _getSourceTargetMappingErrors(): string[] {
		// Validate source/target database mappings:
		var errors: string[] = [];
		const collationErrors: string[] = [];
		const targetDatabaseKeys = new Map<string, string>();
		const migrationDatabaseCount = this._azureResourceTable.dataValues?.length ?? 0;
		this.migrationStateModel._targetDatabaseNames = [];
		const databaseInfosForMigration = new Map(this.migrationStateModel._databaseInfosForMigration.map(o => [o.databaseName, o]));

		if (migrationDatabaseCount === 0) {
			errors.push(constants.SQL_TARGET_MAPPING_ERROR_MISSING_TARGET);
		} else {
			for (let i = 0; i < this.migrationStateModel._databasesForMigration.length; i++) {
				const sourceDatabaseName = this.migrationStateModel._databasesForMigration[i];
				const sourceDatabaseInfo = databaseInfosForMigration.get(sourceDatabaseName);
				const targetDatabaseInfo = this.migrationStateModel._sourceTargetMapping.get(sourceDatabaseName);
				const targetDatabaseName = targetDatabaseInfo?.databaseName;
				const sourceDatabaseCollation = sourceDatabaseInfo?.databaseCollation;
				const targetDatabaseCollation = targetDatabaseInfo?.databaseCollation;
				if (targetDatabaseName && targetDatabaseName.length > 0) {
					if (!targetDatabaseKeys.has(targetDatabaseName)) {
						targetDatabaseKeys.set(targetDatabaseName, sourceDatabaseName);
						this.migrationStateModel._targetDatabaseNames.push(targetDatabaseName);
					} else {
						const mappedSourceDatabaseName = targetDatabaseKeys.get(targetDatabaseName) ?? '';
						// target mapped only once
						errors.push(
							constants.SQL_TARGET_CONNECTION_DUPLICATE_TARGET_MAPPING(
								targetDatabaseName,
								sourceDatabaseName,
								mappedSourceDatabaseName));
					}

					// Collation validation
					if (!this._isCollationSame(sourceDatabaseCollation, targetDatabaseCollation)) {
						collationErrors.push(
							constants.SQL_TARGET_SOURCE_COLLATION_NOT_SAME(
								ValidationErrorCodes.SqlInfoValidationFailed,
								sourceDatabaseName,
								targetDatabaseName,
								sourceDatabaseCollation,
								targetDatabaseCollation));
					}
				} else {
					// source/target has mapping
					errors.push(constants.SQL_TARGET_CONNECTION_SOURCE_NOT_MAPPED(sourceDatabaseName));
				}
			}
		}

		if (collationErrors.length > 0) {
			collationErrors.push(constants.SQL_MIGRATION_TROUBLESHOOTING_LINK);
			errors = errors.concat(collationErrors);
		}

		return errors;
	}

	private async _validateFields(): Promise<void> {
		await this._azureAccountsDropdown.validate();
		await this._accountTenantDropdown.validate();
		await this._azureSubscriptionDropdown.validate();
		await this._azureLocationDropdown.validate();
		await this._azureResourceGroupDropdown.validate();
		await this._azureResourceDropdown.validate();
		await this._targetPasswordInputBox.validate();
		await this._targetUserNameInputBox.validate();
		await this._azureResourceTable.validate();
	}

	private _isCollationSame(sourceDatabaseCollation: string | undefined, targetDatabaseCollation: string | undefined): boolean {
		return sourceDatabaseCollation !== undefined &&
			sourceDatabaseCollation.length > 0 &&
			targetDatabaseCollation !== undefined &&
			targetDatabaseCollation.length > 0 &&
			sourceDatabaseCollation.toLocaleLowerCase() === targetDatabaseCollation.toLocaleLowerCase();
	}

	private _updateNextButton() {
		if (this.migrationStateModel.tdeMigrationConfig.shouldAdsMigrateCertificates() &&
			this.migrationStateModel.tdeMigrationConfig.lastTdeMigrationResult().state !== TdeMigrationState.Succeeded) {
			this.wizard.pages[this.wizard.currentPage + 1].enabled = false;
			this.wizard.nextButton.enabled = false;
		} else {
			this.wizard.pages[this.wizard.currentPage + 1].enabled = true;
			this.wizard.nextButton.enabled = true;
		}
	}

	private _updateTdeMigrationButtonStatus() {
		this.wizard.customButtons[TDE_MIGRATION_BUTTON_INDEX].enabled =
			this.migrationStateModel.tdeMigrationConfig.shouldAdsMigrateCertificates() &&
			this.migrationStateModel._targetManagedInstances.length > 0;
	}

	private _resetTdeMigrationResult() {
		this.migrationStateModel.tdeMigrationConfig.resetTdeMigrationResult();
		this._updateNextButton();
	}

	private async _startTdeMigration(): Promise<void> {
		this._resetTdeMigrationResult();
		const dialog = new TdeMigrationDialog(this.migrationStateModel, this._certMigrationEventEmitter);
		await dialog.openDialog();
	}
}
