import {
	createListResource,
	createResource,
	LoadingIndicator,
} from 'jingrow-ui';
import { defineAsyncComponent, h } from 'vue';
import { unparse } from 'papaparse';
import { toast } from 'vue-sonner';
import AddDomainDialog from '../components/AddDomainDialog.vue';
import GenericDialog from '../components/GenericDialog.vue';
import ObjectList from '../components/ObjectList.vue';
import SiteActions from '../components/SiteActions.vue';
import { getTeam, switchToTeam } from '../data/team';
import router from '../router';
import { getRunningJobs } from '../utils/agentJob';
import { confirmDialog, icon, renderDialog } from '../utils/components';
import dayjs from '../utils/dayjs';
import { bytes, date, userCurrency, getStatusLabel, getDeployTypeLabel } from '../utils/format';
import { getToastErrorMessage } from '../utils/toast';
import { getDocResource } from '../utils/resource';
import { trialDays } from '../utils/site';
import { clusterOptions, getUpsellBanner } from './common';
import { getAppsTab } from './common/apps';
import { isMobile } from '../utils/device';
import { getJobsTab } from './common/jobs';
import { t } from '../utils/i18n';

export default {
	pagetype: 'Site',
	whitelistedMethods: {
		activate: 'activate',
		addDomain: 'add_domain',
		archive: 'archive',
		backup: 'backup',
		clearSiteCache: 'clear_site_cache',
		deactivate: 'deactivate',
		disableReadWrite: 'disable_read_write',
		enableReadWrite: 'enable_read_write',
		installApp: 'install_app',
		uninstallApp: 'uninstall_app',
		migrate: 'migrate',
		moveToBench: 'move_to_bench',
		moveToGroup: 'move_to_group',
		loginAsAdmin: 'login_as_admin',
		loginAsTeam: 'login_as_team',
		isSetupWizardComplete: 'is_setup_wizard_complete',
		reinstall: 'reinstall',
		removeDomain: 'remove_domain',
		redirectToPrimary: 'set_redirect',
		removeRedirect: 'unset_redirect',
		setPrimaryDomain: 'set_host_name',
		restoreSite: 'restore_site',
		restoreSiteFromFiles: 'restore_site_from_files',
		scheduleUpdate: 'schedule_update',
		editScheduledUpdate: 'edit_scheduled_update',
		cancelUpdate: 'cancel_scheduled_update',
		setPlan: 'set_plan',
		updateConfig: 'update_config',
		deleteConfig: 'delete_config',
		sendTransferRequest: 'send_change_team_request',
		addTag: 'add_resource_tag',
		removeTag: 'remove_resource_tag',
		getBackupDownloadLink: 'get_backup_download_link',
		fetchDatabaseTableSchemas: 'fetch_database_table_schemas',
	},
	list: {
		route: '/sites',
		title: t('Sites'),
		fields: [
			'plan.plan_title as plan_title',
			'plan.price_usd as price_usd',
			'plan.price_cny as price_cny',
			'group.title as group_title',
			'group.public as group_public',
			'group.team as group_team',
			'group.version as version',
			'cluster.image as cluster_image',
			'cluster.title as cluster_title',
			'trial_end_date',
			'site_end_date',
		],
		orderBy: 'creation desc',
		searchField: 'host_name',
		filterControls() {
			return [
				{
					type: 'select',
					label: t('Status'),
					fieldname: 'status',
					options: [
						{ label: '', value: '' },
						{ label: t('Active'), value: 'Active' },
						{ label: t('Inactive'), value: 'Inactive' },
						{ label: t('Suspended'), value: 'Suspended' },
						{ label: t('Broken'), value: 'Broken' },
						{ label: t('Archived'), value: 'Archived' },
					],
				},
				{
					type: 'link',
					label: t('Version'),
					fieldname: 'group.version',
					options: {
						pagetype: 'Jingrow Version',
					},
				},
				{
					type: 'link',
					label: t('Release Group'),
					fieldname: 'group',
					options: {
						pagetype: 'Release Group',
					},
				},
				{
					type: 'select',
					label: t('Region'),
					fieldname: 'cluster',
					options: clusterOptions,
				},
				{
					type: 'link',
					label: t('Tag'),
					fieldname: 'tags.tag',
					options: {
						pagetype: 'Jcloud Tag',
						filters: {
							pagetype_name: 'Site',
						},
					},
				},
			];
		},
		columns: [
			{
				label: t('Sites'),
				fieldname: 'host_name',
				width: 1.5,
				class: 'font-medium',
				format(value, row) {
					return value || row.name;
				},
			},
			{
				label: t('Status'),
				fieldname: 'status',
				type: 'Badge',
				width: '140px',
				format(value) {
					const statusMap = {
						'Active': t('Active'),
						'Inactive': t('Inactive'),
						'Suspended': t('Suspended'),
						'Broken': t('Broken'),
						'Archived': t('Archived'),
						'Pending': t('Pending'),
						'Installing': t('Installing'),
						'Update Available': t('Update Available'),
						'Running': t('Running'),
						'Success': t('Success'),
						'Failure': t('Failure')
					};
					return statusMap[value] || value;
				}
			},
			{
				label: t('Plan Type'),
				fieldname: 'plan',
				width: 0.85,
				format(value, row) {
					if (row.trial_end_date) {
						return trialDays(row.trial_end_date);
					}
					const $team = getTeam();
					if (row.price_usd > 0) {
						const china = $team.pg?.currency === 'CNY';
						const formattedValue = userCurrency(
							china ? row.price_cny : row.price_usd,
							0,
						);
						return `${formattedValue}/${t('Monthly')}`;
					}
					return row.plan_title;
				},
			},
			{
				label: t('Region'),
				fieldname: 'cluster',
				width: 1,
				format(value, row) {
					return row.cluster_title || value;
				},
				prefix(row) {
					return h('img', {
						src: row.cluster_image,
						class: 'w-4 h-4',
						alt: row.cluster_title,
					});
				},
			},
			{
				label: t('Release Group'),
				fieldname: 'group',
				width: '15rem',
				format(value, row) {
					return row.group_public ? t('Public') : row.group_title || value;
				},
			},
			{
				label: t('Version'),
				fieldname: 'version',
				width: 0.5,
			},
			{
				label: t('Due Date'),
				fieldname: 'site_end_date',
				width: 1,
				format(value) {
					return value ? date(value, 'YYYY-MM-DD') : '';
				},
			},
		],
		primaryAction({ listResource: sites }) {
			return {
				label: t('New Site'),
				variant: 'outline',
				slots: {
					prefix: icon('plus'),
				},
				onClick() {
					router.push({ name: 'New Site' });
				},
			};
		},
		moreActions({ listResource: sites }) {
			return [
				{
					label: t('Export as CSV'),
					icon: 'download',
					onClick() {
						const fields = [
							'host_name',
							'plan_title',
							'cluster_title',
							'group_title',
							'version',
						];

						const data = sites.data.map((site) => {
							const row = {};
							fields.forEach((field) => {
								row[field] = site[field];
							});
							return row;
						});

						let csv = unparse({
							fields,
							data,
						});
						csv = '\uFEFF' + csv; // for utf-8

						// create a blob and trigger a download
						const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
						const today = new Date().toISOString().split('T')[0];
						const filename = `sites-${today}.csv`;
						const link = document.createElement('a');
						link.href = URL.createObjectURL(blob);
						link.download = filename;
						link.click();
						URL.revokeObjectURL(link.href);
					},
				},
			];
		},
	},
	detail: {
		titleField: 'name',
		route: '/sites/:name',
		statusBadge({ documentResource: site }) {
			const statusMap = {
				'Active': t('Active'),
				'Inactive': t('Inactive'),
				'Suspended': t('Suspended'),
				'Broken': t('Broken'),
				'Archived': t('Archived'),
				'Pending': t('Pending'),
				'Installing': t('Installing'),
				'Running': t('Running'),
				'Success': t('Success'),
				'Failure': t('Failure')
			};
			return { label: statusMap[site.pg.status] || site.pg.status };
		},
		breadcrumbs({ items, documentResource: site }) {
			let breadcrumbs = [];
			let $team = getTeam();
			let siteCrumb = {
				label: site.pg.host_name || site.pg?.name,
				route: `/sites/${site.pg?.name}`,
			};

			if (
				(site.pg.server_team == $team.pg?.name &&
					site.pg.group_team == $team.pg?.name) ||
				$team.pg?.is_desk_user
			) {
				breadcrumbs.push({
					label: site.pg?.server_title || site.pg?.server,
					route: `/servers/${site.pg?.server}`,
				});
			}
			if (
				site.pg.group_team == $team.pg?.name ||
				$team.pg?.is_desk_user ||
				$team.pg?.is_support_agent
			) {
				breadcrumbs.push(
					{
						label: site.pg?.group_title,
						route: `/groups/${site.pg?.group}`,
					},
					siteCrumb,
				);
			} else {
				breadcrumbs.push(...items.slice(0, -1), siteCrumb);
			}
			return breadcrumbs;
		},
		tabs: [
			{
				label: t('Overview'),
				icon: icon('home'),
				route: 'overview',
				type: 'Component',
				condition: (site) => site.pg?.status !== 'Archived',
				component: defineAsyncComponent(
					() => import('../components/SiteOverview.vue'),
				),
				props: (site) => {
					return { site: site.pg?.name };
				},
			},
			getAppsTab(true),
			{
				label: t('Domain'),
				icon: icon('external-link'),
				route: 'domains',
				type: 'list',
				condition: (site) => site.pg?.status !== 'Archived',
				list: {
					pagetype: 'Site Domain',
					fields: ['redirect_to_primary'],
					filters: (site) => {
						return { site: site.pg?.name };
					},
					columns: [
						{
							label: t('Domain'),
							fieldname: 'domain',
						},
						{
							label: t('Status'),
							fieldname: 'status',
							type: 'Badge',
							format(value) {
								const statusMap = {
									'Active': t('Active'),
									'Inactive': t('Inactive'),
									'Suspended': t('Suspended'),
									'Broken': t('Broken'),
									'Archived': t('Archived'),
									'Pending': t('Pending'),
									'Installing': t('Installing'),
									'Running': t('Running'),
									'Success': t('Success'),
									'Failure': t('Failure')
								};
								return statusMap[value] || value;
							}
						},
						{
							label: t('Primary Domain'),
							fieldname: 'primary',
							type: 'Icon',
							Icon(value) {
								return value ? 'check' : '';
							},
						},
						{
							label: t('DNS Type'),
							fieldname: 'dns_type',
							type: 'Badge',
						},
					],
					banner({ documentResource: site }) {
						if (site.pg.broken_domain_error) {
							return {
								title: t('Error occurred while obtaining HTTPS certificate for your domain.'),
								type: 'error',
								button: {
									label: t('View Error'),
									variant: 'outline',
									onClick() {
										renderDialog(
											h(
												GenericDialog,
												{
													options: {
														title: t('Error obtaining certificate'),
														size: 'xl',
													},
												},
												{
													default: () => {
														return h('pre', {
															class:
																'whitespace-pre-wrap text-sm rounded border-2 p-3 border-gray-200 bg-gray-100',
															innerHTML: site.pg.broken_domain_error,
														});
													},
												},
											),
										);
									},
								},
							};
						} else {
							return null;
						}
					},
					primaryAction({ listResource: domains, documentResource: site }) {
						return {
							label: t('Add Domain'),
							slots: {
								prefix: icon('plus'),
							},
							onClick() {
								renderDialog(
									h(AddDomainDialog, {
										site: site.pg,
										onDomainAdded() {
											domains.reload();
										},
									}),
								);
							},
						};
					},
					rowActions({ row, listResource: domains, documentResource: site }) {
						return [
							{
								label: t('Remove'),
								condition: () => row.domain !== site.pg?.name,
								onClick() {
									confirmDialog({
										title: t('Remove Domain'),
										message: t('Are you sure you want to remove domain <b>{domain}</b> from site <b>{site}</b>?', { domain: row.domain, site: site.pg?.name }),
										onSuccess({ hide }) {
											if (site.removeDomain.loading) return;
											toast.promise(
												site.removeDomain.submit({
													domain: row.domain,
												}),
												{
													loading: t('Removing domain...'),
													success: () => {
														hide();
														return t('Domain removed');
													},
													error: (e) => getToastErrorMessage(e),
												},
											);
										},
									});
								},
							},
							{
								label: t('Set as Primary Domain'),
								condition: () => !row.primary && row.status === 'Active',
								onClick() {
									confirmDialog({
										title: t('Set as Primary Domain'),
										message: t('Are you sure you want to set domain <b>{domain}</b> as the primary domain for site <b>{site}</b>?', { domain: row.domain, site: site.pg?.name }),
										onSuccess({ hide }) {
											if (site.setPrimaryDomain.loading) return;
											toast.promise(
												site.setPrimaryDomain.submit({
													domain: row.domain,
												}),
												{
													loading: t('Setting primary domain...'),
													success: () => {
														hide();
														return t('Primary domain set');
													},
													error: (e) => getToastErrorMessage(e),
												},
											);
										},
									});
								},
							},
							{
								label: t('Redirect to Primary Domain'),
								condition: () =>
									!row.primary &&
									!row.redirect_to_primary &&
									row.status === 'Active',
								onClick() {
									confirmDialog({
										title: t('Redirect Domain'),
										message: t('Are you sure you want to redirect domain <b>{domain}</b> to the primary domain of site <b>{site}</b>?', { domain: row.domain, site: site.pg?.name }),
										onSuccess({ hide }) {
											if (site.redirectToPrimary.loading) return;
											toast.promise(
												site.redirectToPrimary.submit({
													domain: row.domain,
												}),
												{
													loading: t('Redirecting domain...'),
													success: () => {
														hide();
														return t('Domain redirected');
													},
													error: (e) => getToastErrorMessage(e),
												},
											);
										},
									});
								},
							},
							{
								label: t('Remove Redirect'),
								condition: () =>
									!row.primary &&
									row.redirect_to_primary &&
									row.status === 'Active',
								onClick() {
									confirmDialog({
										title: t('Remove Redirect'),
										message: t('Are you sure you want to remove redirect from domain <b>{domain}</b> to primary domain of site <b>{site}</b>?', { domain: row.domain, site: site.pg?.name }),
										onSuccess({ hide }) {
											if (site.removeRedirect.loading) return;
											toast.promise(
												site.removeRedirect.submit({
													domain: row.domain,
												}),
												{
													loading: t('Removing redirect...'),
													success: () => {
														hide();
														return t('Redirect removed');
													},
													error: (e) => getToastErrorMessage(e),
												},
											);
										},
									});
								},
							},
						];
					},
				},
			},
			{
				label: t('Backups'),
				icon: icon('archive'),
				route: 'backups',
				type: 'list',
				list: {
					pagetype: 'Site Backup',
					filters: (site) => {
						return {
							site: site.pg?.name,
							files_availability: 'Available',
							status: ['in', ['Pending', 'Running', 'Success']],
						};
					},
					orderBy: 'creation desc',
					fields: [
						'job',
						'status',
						'database_url',
						'public_url',
						'private_url',
						'config_file_url',
						'site',
						'remote_database_file',
						'remote_public_file',
						'remote_private_file',
						'remote_config_file',
						'physical',
					],
					columns: [
						{
							label: t('Timestamp'),
							fieldname: 'creation',
							width: 1,
							format(value) {
								return t('Backed up on {date}', { date: date(value, 'llll') });
							},
						},

						{
							label: t('Database'),
							fieldname: 'database_size',
							width: 0.5,
							format(value) {
								return value ? bytes(value) : '';
							},
						},
						{
							label: t('Public Files'),
							fieldname: 'public_size',
							width: 0.5,
							format(value) {
								return value ? bytes(value) : '';
							},
						},
						{
							label: t('Private Files'),
							fieldname: 'private_size',
							width: 0.5,
							format(value) {
								return value ? bytes(value) : '';
							},
						},
						{
							label: t('Backup with Files'),
							fieldname: 'with_files',
							type: 'Icon',
							width: 0.5,
							Icon(value) {
								return value ? 'check' : '';
							},
						},
						{
							label: t('Offsite Backup'),
							fieldname: 'offsite',
							width: 0.5,
							type: 'Icon',
							Icon(value) {
								return value ? 'check' : '';
							},
						},
					],
					filterControls() {
						return [
							{
								type: 'checkbox',
								label: t('Offsite Backup'),
								fieldname: 'offsite',
							},
						];
					},
					rowActions({ row, documentResource: site }) {
						if (row.status != 'Success') return;


						function confirmDownload(backup, file) {
							const fileNameMap = {
								'database': t('database'),
								'public': t('public files'),
								'private': t('private files'),
								'config': t('config file')
							};
							const fileName = fileNameMap[file] || file;
							const message = !backup.offsite
								? t('You will download the {fileName} backup for site <b>{siteName}</b>, created on {date}.<br><br><div class="p-2 bg-gray-100 border-gray-200 rounded">You need to log in as <b>System Administrator</b> on <em>your site</em> to download the backup.<div>', { fileName, siteName: site.pg?.host_name || site.pg?.name, date: date(backup.creation, 'llll') })
								: t('You will download the {fileName} backup for site <b>{siteName}</b>, created on {date}.', { fileName, siteName: site.pg?.host_name || site.pg?.name, date: date(backup.creation, 'llll') });
							confirmDialog({
								title: t('Download Backup'),
								message: message,
								onSuccess() {
									downloadBackup(backup, file);
								},
							});
						}

						async function downloadBackup(backup, file) {
							// file: database, public, or private
							if (backup.offsite) {
								site.getBackupDownloadLink.submit(
									{ backup: backup.name, file },
									{
										onSuccess(r) {
											// TODO: fix this in documentResource, it should return message directly
											if (r.message) {
												window.open(r.message);
											}
										},
									},
								);
							} else {
								const url =
									file == 'config'
										? backup.config_file_url
										: backup[file + '_url'];

								const domainRegex = /^(https?:\/\/)?([^/]+)\/?/;
								const newUrl = url.replace(
									domainRegex,
									`$1${site.pg.host_name}/`,
								);
								window.open(newUrl);
							}
						}

						return [
							{
								group: t('Details'),
								items: [
									{
										label: t('View Job'),
										onClick() {
											router.push({
												name: 'Site Job',
												params: { name: site.name, id: row.job },
											});
										},
									},
								],
							},
							{
								group: t('Download'),
								items: [
									{
										label: t('Download Database'),
										onClick() {
											return confirmDownload(row, 'database');
										},
									},
									{
										label: t('Download Public Files'),
										onClick() {
											return confirmDownload(row, 'public');
										},
										condition: () => row.public_url,
									},
									{
										label: t('Download Private Files'),
										onClick() {
											return confirmDownload(row, 'private');
										},
										condition: () => row.private_url,
									},
									{
										label: t('Download Config File'),
										onClick() {
											return confirmDownload(row, 'config');
										},
										condition: () => row.config_file_url,
									},
								],
							},
							{
								group: t('Restore'),
								condition: () => row.offsite,
								items: [
									{
										label: t('Restore Backup'),
										condition: () => site.pg.status !== 'Archived',
										onClick() {
											confirmDialog({
												title: t('Restore Backup'),
												message: t('Are you sure you want to restore your site to the offsite backup from <b>{date}</b>?', { date: dayjs(row.creation).format('lll') }),
												onSuccess({ hide }) {
													toast.promise(
														site.restoreSiteFromFiles.submit({
															files: {
																database: row.remote_database_file,
																public: row.remote_public_file,
																private: row.remote_private_file,
																config: row.remote_config_file,
															},
														}),
														{
															loading: t('Scheduling backup restore...'),
															success: (jobId) => {
																hide();
																router.push({
																	name: 'Site Job',
																	params: {
																		name: site.name,
																		id: jobId,
																	},
																});
																return t('Backup restore scheduled successfully.');
															},
															error: (e) => getToastErrorMessage(e),
														},
													);
												},
											});
										},
									},
									{
										label: t('Restore Backup on Another Site'),
										onClick() {
											let SelectSiteForRestore = defineAsyncComponent(
												() =>
													import('../components/site/SelectSiteForRestore.vue'),
											);
											renderDialog(
												h(SelectSiteForRestore, {
													site: site.name,
													onRestore(siteName) {
														const restoreSite = createResource({
															url: 'jcloud.api.site.restore',
														});

														return toast.promise(
															restoreSite.submit({
																name: siteName,
																files: {
																	database: row.remote_database_file,
																	public: row.remote_public_file,
																	private: row.remote_private_file,
																	config: row.remote_config_file,
																},
															}),
															{
																loading: t('Scheduling backup restore...'),
																success: (jobId) => {
																	router.push({
																		name: 'Site Job',
																		params: { name: siteName, id: jobId },
																	});
																	return t('Backup restore scheduled successfully.');
																},
																error: (e) => getToastErrorMessage(e),
															},
														);
													},
												}),
											);
										},
									},
								],
							},
						].filter((d) => (d.condition ? d.condition() : true));
					},
					primaryAction({ listResource: backups, documentResource: site }) {
						return {
							label: t('Schedule Backup'),
							slots: {
								prefix: icon('upload-cloud'),
							},
							loading: site.backup.loading,
							onClick() {
								confirmDialog({
									title: t('Schedule Backup'),
									message: t('Are you sure you want to schedule a backup? This will create a local backup.'),
									onSuccess({ hide }) {
										toast.promise(
											site.backup.submit({
												with_files: true,
											}),
											{
												loading: t('Scheduling backup...'),
												success: () => {
													hide();
													router.push({
														name: 'Site Jobs',
														params: { name: site.name },
													});
													return t('Backup scheduled successfully.');
												},
												error: (e) => getToastErrorMessage(e),
											},
										);
									},
								});
							},
						};
					},
				},
			},
			getJobsTab('Site'),
			{
				label: t('Actions'),
				icon: icon('sliders'),
				route: 'actions',
				type: 'Component',
				condition: (site) => site.pg?.status !== 'Archived',
				component: SiteActions,
				props: (site) => {
					return { site: site.pg?.name };
				},
			},
			{
				label: t('Update'),
				icon: icon('arrow-up-circle'),
				route: 'updates',
				type: 'list',
				condition: (site) => site.pg?.status !== 'Archived',
				childrenRoutes: ['Site Update'],
				list: {
					pagetype: 'Site Update',
					filters: (site) => {
						return { site: site.pg?.name };
					},
					orderBy: 'creation',
					fields: [
						'difference',
						'update_job.end as updated_on',
						'update_job',
						'backup_type',
						'recover_job',
					],
					columns: [
						{
							label: t('Type'),
							fieldname: 'deploy_type',
							width: 0.3,
							format(value) {
								return getDeployTypeLabel(value);
							}
						},
						{
							label: t('Status'),
							fieldname: 'status',
							type: 'Badge',
							width: 0.5,
							format(value) {
								return getStatusLabel(value);
							}
						},
						// {
						// 	label: t('Backup'),
						// 	width: 0.4,
						// 	type: 'Component',
						// 	component({ row }) {
						// 		return h(
						// 			'div',
						// 			{
						// 				class: 'truncate text-base',
						// 			},
						// 			row.skipped_backups
						// 				? t('Skipped')
						// 				: row.backup_type || t('Logical'),
						// 		);
						// 	},
						// },
						{
							label: t('Owner'),
							fieldname: 'owner',
						},
						{
							label: t('Scheduled Time'),
							fieldname: 'scheduled_time',
							format(value) {
								return date(value, 'lll');
							},
						},
						{
							label: t('Updated On'),
							fieldname: 'updated_on',
							format(value) {
								return date(value, 'lll');
							},
						},
					],
					rowActions({ row, documentResource: site }) {
						return [
							{
								label: t('Update'),
								condition: () => row.status === 'Scheduled',
								onClick() {
									let SiteUpdateDialog = defineAsyncComponent(
										() => import('../components/SiteUpdateDialog.vue'),
									);
									renderDialog(
										h(SiteUpdateDialog, {
											site: site.pg?.name,
											existingUpdate: row.name,
										}),
									);
								},
							},
							{
								label: t('Cancel'),
								condition: () => row.status === 'Scheduled',
								onClick() {
									confirmDialog({
										title: t('Cancel Update'),
										message: t('Are you sure you want to cancel the scheduled update?'),
										onSuccess({ hide }) {
											if (site.cancelUpdate.loading) return;
											toast.promise(
												site.cancelUpdate.submit({ site_update: row.name }),
												{
													loading: t('Cancelling update...'),
													success: () => {
														hide();
														site.reload();
														return t('Update cancelled');
													},
													error: (e) => getToastErrorMessage(e),
												},
											);
										},
									});
								},
							},
							{
								label: t('View Job'),
								condition: () => row.status !== 'Scheduled',
								onClick() {
									router.push({
										name: 'Site Update',
										params: { id: row.name },
									});
								},
							},
							{
								label: t('Update Now'),
								condition: () => row.status === 'Scheduled',
								onClick() {
									let siteUpdate = getDocResource({
										pagetype: 'Site Update',
										name: row.name,
										whitelistedMethods: {
											updateNow: 'start',
										},
									});

									toast.promise(siteUpdate.updateNow.submit(), {
										loading: t('Updating site...'),
										success: () => {
											router.push({
												name: 'Site Update',
												params: { id: row.name },
											});

											return t('Site update started');
										},
										error: t('Failed to update site'),
									});
								},
							},
							{
								label: t('View App Changes'),
								onClick() {
									createListResource({
										pagetype: 'Deploy Candidate Difference App',
										fields: [
											'difference.github_diff_url as diff_url',
											'difference.source_hash as source_hash',
											'difference.destination_hash as destination_hash',
											'app.title as app',
										],
										filters: {
											parenttype: 'Deploy Candidate Difference',
											parent: row.difference,
										},
										auto: true,
										pageLength: 99,
										onSuccess(data) {
											if (data?.length) {
												renderDialog(
													h(
														GenericDialog,
														{
															options: {
																title: t('App Changes'),
																size: '2xl',
															},
														},
														{
															default: () =>
																h(ObjectList, {
																	options: {
																		data: () => data,
																		columns: [
																			{
																				label: t('App'),
																				fieldname: 'app',
																			},
																			{
																				label: t('From'),
																				fieldname: 'source_hash',
																				type: 'Button',
																				Button({ row }) {
																					return {
																						label:
																							row.source_tag ||
																							row.source_hash.slice(0, 7),
																						variant: 'ghost',
																						class: 'font-mono',
																						link: `${
																							row.diff_url.split('/compare')[0]
																						}/commit/${row.source_hash}`,
																					};
																				},
																			},
																			{
																				label: t('To'),
																				fieldname: 'destination_hash',
																				type: 'Button',
																				Button({ row }) {
																					return {
																						label:
																							row.destination_tag ||
																							row.destination_hash.slice(0, 7),
																						variant: 'ghost',
																						class: 'font-mono',
																						link: `${
																							row.diff_url.split('/compare')[0]
																						}/commit/${row.destination_hash}`,
																					};
																				},
																			},
																			{
																				label: t('App Changes'),
																				fieldname: 'diff_url',
																				align: 'right',
																				type: 'Button',
																				Button({ row }) {
																					return {
																						label: t('View'),
																						variant: 'ghost',
																						slots: {
																							prefix: icon('external-link'),
																						},
																						link: row.diff_url,
																					};
																				},
																			},
																		],
																	},
																}),
														},
													),
												);
											} else toast.error(t('No app changes found'));
										},
									});
								},
							},
						];
					},
					actions({ documentResource: site }) {
						if (site.pg.group_public) return [];

						return [
							{
								label: t('Config'),
								slots: {
									prefix: icon('settings'),
								},
								onClick() {
									let ConfigureAutoUpdateDialog = defineAsyncComponent(
										() =>
											import(
												'../components/site/ConfigureAutoUpdateDialog.vue'
											),
									);

									renderDialog(
										h(ConfigureAutoUpdateDialog, {
											site: site.pg?.name,
										}),
									);
								},
							},
						];
					},
				},
			},

		],
		actions(context) {
			let { documentResource: site } = context;
			let $team = getTeam();
			let runningJobs = getRunningJobs({ site: site.pg?.name });

			return [
				{
					label: t('Jobs in Progress'),
					slots: {
						prefix: () => h(LoadingIndicator, { class: 'w-4 h-4' }),
					},
					condition() {
						return (
							runningJobs.filter((job) =>
								['Pending', 'Running'].includes(job.status),
							).length > 0
						);
					},
					onClick() {
						router.push({
							name: 'Site Jobs',
							params: { name: site.name },
						});
					},
				},
				{
					label: t('Update Available'),
					variant: site.pg?.setup_wizard_complete ? 'solid' : 'subtle',
					slots: {
						prefix: icon('alert-circle'),
					},
					condition() {
						return (
							!site.pg?.has_scheduled_updates &&
							site.pg.update_information?.update_available &&
							['Active', 'Inactive', 'Suspended', 'Broken'].includes(
								site.pg.status,
							)
						);
					},

					onClick() {
						let SiteUpdateDialog = defineAsyncComponent(
							() => import('../components/SiteUpdateDialog.vue'),
						);
						renderDialog(h(SiteUpdateDialog, { site: site.pg?.name }));
					},
				},
				{
					label: t('Scheduled Updates'),
					slots: {
						prefix: icon('calendar'),
					},
					condition: () => site.pg?.has_scheduled_updates,
					onClick() {
						router.push({
							name: 'Site Detail Updates',
							params: { name: site.name },
						});
					},
				},
				{
					label: t('Impersonate Site Owner'),
					title: t('Impersonate Site Owner'), // for label to pop-up on hover
					slots: {
						icon: defineAsyncComponent(
							() => import('~icons/lucide/venetian-mask'),
						),
					},
					condition: () =>
						$team.pg?.is_desk_user && site.pg.team !== $team.name,
					onClick() {
						switchToTeam(site.pg.team);
					},
				},
				{
					label: t('Visit Site'),
					slots: {
						prefix: icon('external-link'),
					},
					condition: () =>
						site.pg.status !== 'Archived' && site.pg?.setup_wizard_complete,
					onClick() {
						window.open(`https://${site.name}/app`, '_blank');
					},
				},
				{
					label: t('Setup Site'),
					slots: {
						prefix: icon('external-link'),
					},
					variant: 'solid',
					condition: () =>
						site.pg.status === 'Active' && !site.pg?.setup_wizard_complete,
					onClick() {
						if (site.pg.additional_system_user_created) {
							site.loginAsTeam
								.submit({ reason: '' })
								.then((url) => window.open(url, '_blank'));
						} else {
							site.loginAsAdmin
								.submit({ reason: '' })
								.then((url) => window.open(url, '_blank'));
						}
					},
				},
				{
					label: t('Options'),
					context,
					options: [
						{
							label: t('View in Desk'),
							icon: 'external-link',
							condition: () => $team.pg?.is_desk_user,
							onClick: () => {
								window.open(
									`${window.location.protocol}//${window.location.host}/app/site/${site.name}`,
									'_blank',
								);
							},
						},
						{
							label: t('Login as Administrator'),
							icon: 'external-link',
							condition: () => ['Active', 'Broken'].includes(site.pg.status),
							onClick: () => {
								confirmDialog({
									title: t('Login as Administrator'),
									message: t('Are you sure you want to login as administrator to site <b>{site}</b>?', { site: site.pg?.name }),
									fields:
										$team.name !== site.pg.team
											? [
													{
														label: t('Reason'),
														type: 'textarea',
														fieldname: 'reason',
													},
												]
											: [],
									onSuccess: ({ hide, values }) => {
										if (!values.reason && $team.name !== site.pg.team) {
											throw new Error(t('Reason is required'));
										}
										return site.loginAsAdmin
											.submit({ reason: values.reason })
											.then((result) => {
												let url = result;
												window.open(url, '_blank');
												hide();
											});
									},
								});
							},
						},
					],
				},
			];
		},
	},

	routes: [
		{
			name: t('Site Update'),
			path: 'updates/:id',
			component: () => import('../pages/SiteUpdate.vue'),
		},
		{
			name: 'Site Job',
			path: 'jobs/:id',
			component: () => import('../pages/JobPage.vue')
		}
	],
};