<script lang="ts">
	import { type Job, JobService } from '$lib/gen'
	import { base } from '$lib/base'
	import Button from '$lib/components/common/button/Button.svelte'
	import CenteredModal from '$lib/components/CenteredModal.svelte'
	import { sendUserToast } from '$lib/toast'
	import FlowMetadata from '$lib/components/FlowMetadata.svelte'
	import JobArgs from '$lib/components/JobArgs.svelte'
	import { onDestroy, onMount, untrack } from 'svelte'
	import Tooltip from '$lib/components/Tooltip.svelte'
	import SchemaForm from '$lib/components/SchemaForm.svelte'
	import { enterpriseLicense, userStore, workspaceStore } from '$lib/stores'
	import { LogIn, AlertTriangle, ExternalLink } from 'lucide-svelte'
	import { mergeSchema } from '$lib/common'
	import { emptyString } from '$lib/utils'
	import { Alert } from '$lib/components/common'
	import { getUserExt } from '$lib/user'
	import { setLicense } from '$lib/enterpriseUtils'
	import DisplayResult from '$lib/components/DisplayResult.svelte'
	import ScheduleEditor from '$lib/components/triggers/schedules/ScheduleEditor.svelte'
	import FlowGraphV2 from '$lib/components/graph/FlowGraphV2.svelte'
	import { page } from '$app/state'

	$workspaceStore = page.params.workspace
	let rd = page.url.href.replace(page.url.origin, '')

	let job: Job | undefined = $state(undefined)
	let currentApprovers: { resume_id: number; approver: string }[] = $state([])
	let viewToken: string | undefined = $state(undefined)
	// Whether the current visitor is a logged-in member of this workspace — if so we can
	// surface a clear, ready-to-use run-details link (the view token grants them access).
	let isWorkspaceMember = $state(false)
	let approver = page.url.searchParams.get('approver') ?? undefined

	let completed: boolean = $state(false)

	let dynamicSchema: any = $state({})

	let timeout: number | undefined = undefined
	let error: string | undefined = $state(undefined)
	let default_payload: any = $state({})
	let enum_payload: object = $state({})
	let description: any = $state(undefined)

	setLicense()

	onMount(() => {
		window.onunhandledrejection = (event: PromiseRejectionEvent) => {
			event.preventDefault()
			timeout && clearInterval(timeout)
			if (event.reason?.message) {
				const { message, body } = event.reason

				if (body) {
					sendUserToast(`${body}`, true)
					error = body.toString()
				} else {
					sendUserToast(`${message}`, true)
					error = message.toString()
				}
			} else {
				console.log('Caught unhandled promise rejection without message', event)
			}
		}
		getJob()
		timeout = setInterval(getJob, 1000)
		getUserExt(page.params.workspace ?? '').then((u) => (isWorkspaceMember = !!u))
	})

	onDestroy(() => {
		timeout && clearInterval(timeout)
	})

	let argsFetched = $state(false)

	let valid = $state(true)
	async function getDefaultArgs() {
		argsFetched = true
		let jobId = job?.flow_status?.modules?.[approvalStep]?.job
		if (!jobId) {
			return {}
		}
		let job_result = (await JobService.getCompletedJobResult({
			workspace: job?.workspace_id ?? '',
			id: jobId,
			secret: page.params.hmac,
			suspendedJob: page.params.job,
			resumeId: new Number(page.params.resume).valueOf(),
			approver
		})) as any
		description = job_result?.description
		default_payload = job_result?.default_args ?? {}
		enum_payload = job_result?.enums ?? {}
		if (job_result?.schema) {
			dynamicSchema = job_result?.schema ?? {}
		}
	}

	async function getJob() {
		const suspendedJobFlow = await JobService.getSuspendedJobFlow({
			workspace: page.params.workspace ?? '',
			id: page.params.job ?? '',
			resumeId: new Number(page.params.resume).valueOf(),
			signature: page.params.hmac ?? '',
			approver
		})
		job = suspendedJobFlow.job as Job
		currentApprovers = suspendedJobFlow.approvers
		viewToken = suspendedJobFlow.view_token
	}

	async function resume() {
		await JobService.resumeSuspendedJobPost({
			workspace: page.params.workspace ?? '',
			id: page.params.job ?? '',
			resumeId: new Number(page.params.resume).valueOf(),
			signature: page.params.hmac ?? '',
			approver,
			requestBody: default_payload
		})
		sendUserToast('Flow approved')
		getJob()
	}

	async function cancel() {
		await JobService.cancelSuspendedJobPost({
			workspace: page.params.workspace ?? '',
			id: page.params.job ?? '',
			resumeId: new Number(page.params.resume).valueOf(),
			signature: page.params.hmac ?? '',
			approver,
			requestBody: {}
		})
		sendUserToast('Flow denied!')
		getJob()
	}

	async function loadUser() {
		userStore.set(await getUserExt(page.params.workspace ?? ''))
	}

	let scheduleEditor: ScheduleEditor | undefined = $state(undefined)
	$effect(() => {
		completed = job?.type == 'CompletedJob'
	})
	let alreadyResumed = $derived(
		currentApprovers
			.map((x) => x.resume_id)
			.includes(new Number(page.params.resume ?? '').valueOf())
	)
	let approvalStep = $derived.by(() => (job?.flow_status?.step ?? 1) - 1)
	// Carry the share-read-link token so a workspace-member approver can open the run
	// details of a flow they don't otherwise have read access to.
	let runDetailsHref = $derived.by(() => {
		let url = `${base}/run/${job?.id}?workspace=${job?.workspace_id}`
		if (viewToken) {
			url += `&view_token=${encodeURIComponent(viewToken)}`
		}
		return url
	})
	let schema = $derived.by(
		() => job?.raw_flow?.modules?.[approvalStep]?.suspend?.resume_form?.schema ?? dynamicSchema
	)
	$effect(() => {
		job && !argsFetched && untrack(() => getDefaultArgs())
	})
	$effect(() => {
		if (job?.raw_flow?.modules?.[approvalStep]?.suspend?.user_auth_required && !$userStore) {
			untrack(() => loadUser())
		}
	})
</script>

<ScheduleEditor bind:this={scheduleEditor} />

<CenteredModal title="Approval for resuming of flow" disableLogo centerVertically={false}>
	{#if error}
		<div class="space-y-6">
			{#if error.startsWith('Not authorized:')}
				<div class="flex flex-row gap-4 justify-center">
					<AlertTriangle />
					<p class="text-lg">Not Authorized</p>
				</div>
				<p class="text-sm">{error.replace(/^(Not authorized: )/, '')}</p>
				<Button href={`/user/login?${rd ? 'rd=' + encodeURIComponent(rd) : ''}`}>
					Sign in
					<LogIn class="w-8" size={18} />
				</Button>
			{:else}
				<div class="flex flex-row gap-4 justify-center">
					<AlertTriangle class="" />
					<p class="text-lg">Permission denied</p>
				</div>
				<p class="text-sm">{error.replace(/^(Permission denied: )/, '')}</p>
			{/if}
		</div>
	{:else}
		<div class="flex flex-row justify-between flex-wrap sm:flex-nowrap gap-x-4">
			<div class="w-full">
				<h2 class="text-sm font-semibold text-emphasis">Approvers</h2>

				<div class="mt-2 text-xs font-normal text-primary">
					{#if currentApprovers.length > 0}
						<ul>
							{#each currentApprovers as approver}
								<li>
									<p>
										{approver.approver}
										<Tooltip>Unique id of approval: {approver.resume_id}</Tooltip>
									</p>
								</li>
							{/each}
						</ul>
					{:else}
						<p class="text-xs text-secondary"
							>No current approvers for this step (approval steps can require more than one
							approval)</p
						>
					{/if}
				</div>
			</div>
			<div class="w-full">
				{#if job && job.raw_flow}
					<FlowMetadata {job} {scheduleEditor} />
				{/if}
			</div>
		</div>
		{#if !completed}
			<h2 class="mt-4 mb-2 text-sm font-semibold text-emphasis">Flow arguments</h2>

			<JobArgs
				id={job?.id}
				workspace={job?.workspace_id ?? $workspaceStore ?? 'no_w'}
				args={job?.args}
			/>
		{/if}

		<div class="mt-8"></div>

		<div class="p-4 rounded-md bg-surface-tertiary shadow-md">
			{#if approver}
				<p class="text-xs font-normal text-primary mb-2"
					>Approving as: <b class="font-semibold text-emphasis">{approver}</b></p
				>
			{/if}

			{#if completed}
				<Alert type="info" title="Flow completed"
					>The flow is not running anymore. You cannot cancel or resume it.
				</Alert>
			{:else if alreadyResumed}
				<Alert type="info" title="Flow already resumed">
					You have already approved this flow to be resumed
				</Alert>
			{/if}

			{#if description != undefined}
				<DisplayResult noControls result={description} />
			{/if}

			{#if schema && Object.keys(schema).length > 0 && !completed}
				{#if emptyString($enterpriseLicense)}
					<Alert type="warning" title="Adding a form to the approval page is an EE feature" />
				{:else}
					<SchemaForm
						onlyMaskPassword
						noVariablePicker
						bind:isValid={valid}
						schema={mergeSchema(schema, enum_payload)}
						bind:args={default_payload}
					/>
				{/if}
			{/if}

			{#if !completed}
				<div class="w-max-md flex flex-row gap-x-4 gap-y-4 justify-between w-full flex-wrap">
					{#if !job?.raw_flow?.modules?.[approvalStep]?.suspend?.hide_cancel}
						<Button
							variant="accent"
							destructive
							on:click|once={cancel}
							size="lg"
							disabled={completed || alreadyResumed}>Deny</Button
						>
					{:else}
						<div></div>
					{/if}

					<Button
						variant="accent"
						on:click|once={resume}
						size="lg"
						disabled={completed || alreadyResumed || !valid}>Approve</Button
					>
				</div>
			{/if}
			{#if !completed && !alreadyResumed && job?.raw_flow?.modules?.[approvalStep]?.suspend?.user_auth_required && job?.raw_flow?.modules?.[approvalStep]?.suspend?.self_approval_disabled && $userStore && $userStore.email === job.email && ($userStore.is_admin || $userStore.is_super_admin)}
				<div class="mt-2">
					<Alert type="warning" title="Warning">
						As an administrator, by resuming or cancelling this stage of the flow, you bypass the
						self-approval interdiction.
					</Alert>
				</div>
			{/if}
		</div>

		<div class="mt-4 flex flex-row flex-wrap justify-between">
			{#if isWorkspaceMember}
				<Button
					size="xs"
					variant="accent-secondary"
					href={runDetailsHref}
					target="_blank"
					endIcon={{ icon: ExternalLink }}
				>
					Open run details
				</Button>
			{:else}
				<a class="text-accent text-xs" target="_blank" rel="noreferrer" href={runDetailsHref}
					>Open run details (require auth) <ExternalLink size={12} class="inline" /></a
				>
			{/if}
		</div>
		{#if job && job.raw_flow && !completed}
			<h2 class="mt-10 text-sm font-semibold text-emphasis mb-2">Flow details</h2>
			<div class="rounded-md overflow-hidden">
				<FlowGraphV2
					workspace={job.workspace_id}
					triggerNode={false}
					earlyStop={job.raw_flow?.skip_expr !== undefined}
					cache={job.raw_flow?.cache_ttl !== undefined}
					modules={job.raw_flow?.modules}
					groups={job.raw_flow?.groups}
					failureModule={job.raw_flow?.failure_module}
					preprocessorModule={job.raw_flow?.preprocessor_module}
					notSelectable
				/>
			</div>
		{/if}
	{/if}
</CenteredModal>
