"use client"

import {
	createContext,
	FunctionComponent,
	ReactNode,
	useCallback,
	useContext,
	useEffect,
	useMemo,
	useRef,
	useState,
} from "react"
import { useQueryClient } from "@tanstack/react-query"

import api from "@shared/api"
import {
	getProjectWorkspaceQueryKey,
	isWorkflowConflictError,
	type ProjectWorkspaceRead,
	type SilenceSettingsPayload,
	type WorkflowActionRequest,
	type WorkflowCutRegionPayload,
	type WorkflowScreen,
	type WorkflowTranscriptionRequest,
	useProjectWorkspaceQuery,
	useWorkflowAction,
} from "@shared/api/projectWorkflow"

/* ------------------------------------------------------------------ */
/*  Step definitions                                                  */
/* ------------------------------------------------------------------ */

export const WIZARD_STEPS = [
	{
		key: "upload",
		label: "Загрузка",
		shortLabel: "Загрузка",
		phaseLabel: "Загрузка файла",
	},
	{
		key: "verify",
		label: "Проверка",
		shortLabel: "Проверка",
		phaseLabel: "Загрузка файла",
	},
	{
		key: "silence-settings",
		label: "Настройка",
		shortLabel: "Настройка",
		phaseLabel: "Удаление тишины",
	},
	{
		key: "processing",
		label: "Обработка",
		shortLabel: "Обработка",
		phaseLabel: "Удаление тишины",
	},
	{
		key: "fragments",
		label: "Настройка вырезок",
		shortLabel: "Вырезки",
		phaseLabel: "Удаление тишины",
	},
	{
		key: "silence-apply-processing",
		label: "Применение вырезок",
		shortLabel: "Применение",
		phaseLabel: "Удаление тишины",
	},
	{
		key: "transcription-settings",
		label: "Настройка",
		shortLabel: "Настройка",
		phaseLabel: "Транскрибация",
	},
	{
		key: "transcription-processing",
		label: "Обработка",
		shortLabel: "Обработка",
		phaseLabel: "Транскрибация",
	},
	{
		key: "subtitle-revision",
		label: "Правка текста",
		shortLabel: "Правка",
		phaseLabel: "Транскрибация",
	},
	{
		key: "caption-settings",
		label: "Выбор стиля субтитров",
		shortLabel: "Стиль",
		phaseLabel: "Рендер",
	},
	{
		key: "caption-processing",
		label: "Рендер",
		shortLabel: "Рендер",
		phaseLabel: "Рендер",
	},
	{
		key: "caption-result",
		label: "Результат",
		shortLabel: "Результат",
		phaseLabel: "Рендер",
	},
] as const

export type WizardStepKey = (typeof WIZARD_STEPS)[number]["key"]
export type SilenceSettings = SilenceSettingsPayload

const DEFAULT_SILENCE_SETTINGS: SilenceSettings = {
	min_silence_duration_ms: 200,
	silence_threshold_db: 16,
	padding_ms: 100,
}

const DEFAULT_BACK_STEP_MAP: Partial<Record<WizardStepKey, WizardStepKey>> = {
	verify: "upload",
	"silence-settings": "verify",
	processing: "silence-settings",
	fragments: "silence-settings",
	"silence-apply-processing": "fragments",
	"transcription-settings": "fragments",
	"transcription-processing": "transcription-settings",
	"subtitle-revision": "transcription-settings",
	"caption-settings": "subtitle-revision",
	"caption-processing": "caption-settings",
	"caption-result": "caption-settings",
}

type JobOverride =
	| {
			jobId: string | null
			jobType: string | null
	  }
	| undefined

interface WizardContextValue {
	projectId: string
	revision: number
	currentStep: WizardStepKey
	stepIndex: number
	completedSteps: WizardStepKey[]
	primaryFileId: string | null
	primaryFileKey: string | null
	videoUrl: string | null
	originalFileName: string | null
	silenceSettings: SilenceSettings
	activeJobId: string | null
	activeJobType: string | null
	silenceJobId: string | null
	transcriptionArtifactId: string | null
	captionPresetId: string | null
	captionStyleConfig: Record<string, unknown> | null
	captionedVideoPath: string | null
	captionedVideoFileId: string | null
	goToStep: (step: WizardStepKey) => void
	goNext: () => void
	goBack: () => void
	setFileKey: (
		key: string | null,
		fileId: string | null,
		originalFileName?: string | null,
	) => Promise<void>
	setSilenceSettings: (settings: SilenceSettings) => void
	setActiveJob: (jobId: string | null, jobType?: string | null) => void
	startProcessingJob: (
		jobId: string,
		jobType: string,
		processingStep: WizardStepKey,
		completedStep?: WizardStepKey,
	) => void
	setSilenceJobId: (jobId: string | null) => void
	setTranscriptionArtifactId: (id: string | null) => void
	setCaptionPresetId: (id: string | null) => Promise<void>
	setCaptionStyleConfig: (config: Record<string, unknown> | null) => void
	setCaptionedVideoPath: (path: string | null) => void
	setCaptionedVideoFileId: (id: string | null) => void
	markStepCompleted: (step: WizardStepKey) => void
	confirmVerify: () => Promise<void>
	startMediaConvert: () => Promise<void>
	saveSilenceSettings: (settings?: SilenceSettings) => Promise<void>
	startSilenceDetect: (settings?: SilenceSettings) => Promise<void>
	saveSilenceCuts: (cuts: WorkflowCutRegionPayload[]) => Promise<void>
	skipSilenceApply: () => Promise<void>
	startSilenceApply: (cuts: WorkflowCutRegionPayload[]) => Promise<void>
	reopenSilenceReview: () => Promise<void>
	startTranscription: (request: WorkflowTranscriptionRequest) => Promise<void>
	reopenTranscriptionConfig: () => Promise<void>
	markTranscriptionReviewed: () => Promise<void>
	selectCaptionPreset: (presetId: string | null) => Promise<void>
	startCaptionRender: () => Promise<void>
	reopenCaptionConfig: () => Promise<void>
}

const WizardContext = createContext<WizardContextValue | null>(null)

function normalizeWorkspaceStep(
	screen: WorkflowScreen | null | undefined,
): WizardStepKey {
	const fallbackStep: WizardStepKey = "upload"
	if (!screen) return fallbackStep
	return WIZARD_STEPS.some((step) => step.key === screen)
		? (screen as WizardStepKey)
		: fallbackStep
}

function getCompletedSteps(currentStep: WizardStepKey): WizardStepKey[] {
	const currentStepIndex = WIZARD_STEPS.findIndex(
		(step) => step.key === currentStep,
	)
	if (currentStepIndex <= 0) return []
	return WIZARD_STEPS.slice(0, currentStepIndex).map((step) => step.key)
}

export const WizardProvider: FunctionComponent<{
	projectId: string
	children: ReactNode
}> = ({ projectId, children }) => {
	const queryClient = useQueryClient()
	const { data: workspace } = useProjectWorkspaceQuery(projectId)
	const workflowAction = useWorkflowAction(projectId)

	const [stepOverride, setStepOverride] = useState<WizardStepKey | null>(null)
	const [activeJobOverride, setActiveJobOverride] = useState<JobOverride>(
		undefined,
	)
	const [silenceSettingsDraft, setSilenceSettingsDraft] =
		useState<SilenceSettings>(DEFAULT_SILENCE_SETTINGS)
	const [captionPresetDraft, setCaptionPresetDraft] = useState<
		string | null | undefined
	>(undefined)
	const [captionStyleConfigDraft, setCaptionStyleConfigDraft] = useState<
		Record<string, unknown> | null | undefined
	>(undefined)
	const latestRevisionRef = useRef<number | null>(null)

	useEffect(() => {
		if (!workspace) return

		if (latestRevisionRef.current !== workspace.revision) {
			latestRevisionRef.current = workspace.revision
			setStepOverride(null)
			setActiveJobOverride(undefined)
		}

		setSilenceSettingsDraft(
			workspace.silence.settings ?? DEFAULT_SILENCE_SETTINGS,
		)
		setCaptionPresetDraft(workspace.captions.preset_id ?? null)
		setCaptionStyleConfigDraft(workspace.captions.style_config ?? null)
	}, [workspace])

	const workspaceCurrentStep = normalizeWorkspaceStep(workspace?.current_screen)
	const currentStep = stepOverride ?? workspaceCurrentStep
	const completedSteps = useMemo(
		() => getCompletedSteps(currentStep),
		[currentStep],
	)
	const revision = workspace?.revision ?? 0

	const primaryFileId =
		workspace?.source_file_id ??
		workspace?.silence.applied_output_file_id ??
		null

	const { data: primaryFile } = api.useQuery(
		"get",
		"/api/files/files/{file_id}/",
		{ params: { path: { file_id: primaryFileId ?? "" } } },
		{ enabled: !!primaryFileId },
	)

	const { data: primaryFileInfo } = api.useQuery(
		"get",
		"/api/files/files/{file_id}/resolve/",
		{ params: { path: { file_id: primaryFileId ?? "" } } },
		{ enabled: !!primaryFileId },
	)

	const captionedVideoFileId = workspace?.captions.output_file_id ?? null
	const { data: captionedVideoInfo } = api.useQuery(
		"get",
		"/api/files/files/{file_id}/resolve/",
		{ params: { path: { file_id: captionedVideoFileId ?? "" } } },
		{ enabled: !!captionedVideoFileId },
	)

	const primaryFileKey = primaryFile?.path ?? primaryFileInfo?.file_path ?? null
	const videoUrl = primaryFileInfo?.file_url ?? null
	const originalFileName =
		primaryFile?.original_filename ?? primaryFileInfo?.filename ?? null

	const workspaceActiveJob = workspace?.active_job ?? null
	const activeJobId =
		activeJobOverride === undefined
			? (workspaceActiveJob?.job_id ?? null)
			: activeJobOverride.jobId
	const activeJobType =
		activeJobOverride === undefined
			? (workspaceActiveJob?.job_type ?? null)
			: activeJobOverride.jobType

	const silenceJobId = workspace?.silence.detect_job_id ?? null
	const transcriptionArtifactId = workspace?.transcription.artifact_id ?? null
	const captionPresetId =
		captionPresetDraft === undefined
			? (workspace?.captions.preset_id ?? null)
			: captionPresetDraft
	const captionStyleConfig =
		captionStyleConfigDraft === undefined
			? (workspace?.captions.style_config ?? null)
			: captionStyleConfigDraft
	const captionedVideoPath = captionedVideoInfo?.file_path ?? null

	const { data: activeTaskStatus } = api.useQuery(
		"get",
		"/api/tasks/status/{job_id}/",
		{ params: { path: { job_id: activeJobId ?? "" } } },
		{
			enabled: !!activeJobId,
			refetchInterval: 2000,
		},
	)

	useEffect(() => {
		if (!projectId || !activeTaskStatus?.status) return

		if (
			activeTaskStatus.status !== "DONE" &&
			activeTaskStatus.status !== "FAILED" &&
			activeTaskStatus.status !== "CANCELLED"
		) {
			return
		}

		queryClient.invalidateQueries({
			queryKey: getProjectWorkspaceQueryKey(projectId),
		})
	}, [activeTaskStatus?.status, projectId, queryClient])

	const performWorkflowAction = useCallback(
		async (
			action: WorkflowActionRequest,
			options?: {
				optimisticStep?: WizardStepKey | null
				optimisticActiveJob?: JobOverride
			},
		): Promise<ProjectWorkspaceRead | null> => {
			if (!projectId) return null

			if (options?.optimisticStep !== undefined) {
				setStepOverride(options.optimisticStep)
			}

			if (options?.optimisticActiveJob !== undefined) {
				setActiveJobOverride(options.optimisticActiveJob)
			}

			try {
				return await workflowAction.mutateAsync(action)
			} catch (error) {
				if (options?.optimisticStep !== undefined) {
					setStepOverride(null)
				}

				if (options?.optimisticActiveJob !== undefined) {
					setActiveJobOverride(undefined)
				}

				if (isWorkflowConflictError(error)) {
					return null
				}

				throw error
			}
		},
		[projectId, workflowAction],
	)

	const goToStep = useCallback((step: WizardStepKey) => {
		setStepOverride(step)
	}, [])

	const goNext = useCallback(() => {
		const currentStepIndex = WIZARD_STEPS.findIndex(
			(step) => step.key === currentStep,
		)
		if (currentStepIndex < 0 || currentStepIndex >= WIZARD_STEPS.length - 1) {
			return
		}

		setStepOverride(WIZARD_STEPS[currentStepIndex + 1].key)
	}, [currentStep])

	const goBack = useCallback(() => {
		const targetStep = DEFAULT_BACK_STEP_MAP[currentStep]
		if (!targetStep) return
		setStepOverride(targetStep)
	}, [currentStep])

	const setFileKey = useCallback(
		async (
			_key: string | null,
			fileId: string | null,
			_fileName?: string | null,
		) => {
			if (!workspace) return

			if (!fileId) {
				await performWorkflowAction(
					{
						type: "RESET_SOURCE_FILE",
						revision: workspace.revision,
					},
					{
						optimisticStep: "upload",
						optimisticActiveJob: { jobId: null, jobType: null },
					},
				)
				return
			}

			await performWorkflowAction(
				{
					type: "SET_SOURCE_FILE",
					revision: workspace.revision,
					file_id: fileId,
				},
				{
					optimisticStep: "verify",
				},
			)
		},
		[performWorkflowAction, workspace],
	)

	const setSilenceSettings = useCallback((settings: SilenceSettings) => {
		setSilenceSettingsDraft(settings)
	}, [])

	const setActiveJob = useCallback(
		(jobId: string | null, jobType?: string | null) => {
			setActiveJobOverride({
				jobId,
				jobType: jobType ?? null,
			})
		},
		[],
	)

	const startProcessingJob = useCallback(
		(
			jobId: string,
			jobType: string,
			processingStep: WizardStepKey,
			_completedStep?: WizardStepKey,
		) => {
			setActiveJobOverride({
				jobId,
				jobType,
			})
			setStepOverride(processingStep)
		},
		[],
	)

	const setSilenceJobId = useCallback((_jobId: string | null) => {}, [])
	const setTranscriptionArtifactId = useCallback((_id: string | null) => {}, [])
	const setCaptionStyleConfig = useCallback(
		(config: Record<string, unknown> | null) => {
			setCaptionStyleConfigDraft(config)
		},
		[],
	)
	const setCaptionedVideoPath = useCallback((_path: string | null) => {}, [])
	const setCaptionedVideoFileId = useCallback((_id: string | null) => {}, [])
	const markStepCompleted = useCallback((_step: WizardStepKey) => {}, [])

	const confirmVerify = useCallback(async () => {
		if (!workspace) return
		await performWorkflowAction(
			{
				type: "CONFIRM_VERIFY",
				revision: workspace.revision,
			},
			{
				optimisticStep: "silence-settings",
			},
		)
	}, [performWorkflowAction, workspace])

	const startMediaConvert = useCallback(async () => {
		if (!workspace) return
		await performWorkflowAction({
			type: "START_MEDIA_CONVERT",
			revision: workspace.revision,
			output_format: "mp4",
		})
	}, [performWorkflowAction, workspace])

	const saveSilenceSettings = useCallback(
		async (settings?: SilenceSettings) => {
			if (!workspace) return

			const nextSettings = settings ?? silenceSettingsDraft
			setSilenceSettingsDraft(nextSettings)

			await performWorkflowAction({
				type: "SET_SILENCE_SETTINGS",
				revision: workspace.revision,
				settings: nextSettings,
			})
		},
		[performWorkflowAction, silenceSettingsDraft, workspace],
	)

	const startSilenceDetect = useCallback(
		async (settings?: SilenceSettings) => {
			if (!workspace) return

			const nextSettings = settings ?? silenceSettingsDraft
			setSilenceSettingsDraft(nextSettings)

			const updatedWorkspace = await performWorkflowAction({
				type: "SET_SILENCE_SETTINGS",
				revision: workspace.revision,
				settings: nextSettings,
			})

			await performWorkflowAction(
				{
					type: "START_SILENCE_DETECT",
					revision: updatedWorkspace?.revision ?? workspace.revision,
				},
				{
					optimisticStep: "processing",
				},
			)
		},
		[performWorkflowAction, silenceSettingsDraft, workspace],
	)

	const saveSilenceCuts = useCallback(
		async (cuts: WorkflowCutRegionPayload[]) => {
			if (!workspace) return
			await performWorkflowAction({
				type: "SET_SILENCE_CUTS",
				revision: workspace.revision,
				cuts,
			})
		},
		[performWorkflowAction, workspace],
	)

	const skipSilenceApply = useCallback(async () => {
		if (!workspace) return
		await performWorkflowAction(
			{
				type: "SKIP_SILENCE_APPLY",
				revision: workspace.revision,
			},
			{
				optimisticStep: "transcription-settings",
			},
		)
	}, [performWorkflowAction, workspace])

	const startSilenceApply = useCallback(
		async (cuts: WorkflowCutRegionPayload[]) => {
			if (!workspace) return

			const updatedWorkspace = await performWorkflowAction({
				type: "SET_SILENCE_CUTS",
				revision: workspace.revision,
				cuts,
			})

			await performWorkflowAction(
				{
					type: "START_SILENCE_APPLY",
					revision: updatedWorkspace?.revision ?? workspace.revision,
					cuts,
				},
				{
					optimisticStep: "silence-apply-processing",
				},
			)
		},
		[performWorkflowAction, workspace],
	)

	const reopenSilenceReview = useCallback(async () => {
		if (!workspace) return
		await performWorkflowAction(
			{
				type: "REOPEN_SILENCE_REVIEW",
				revision: workspace.revision,
			},
			{
				optimisticStep: "fragments",
			},
		)
	}, [performWorkflowAction, workspace])

	const startTranscription = useCallback(
		async (request: WorkflowTranscriptionRequest) => {
			if (!workspace) return

			await performWorkflowAction(
				{
					type: "START_TRANSCRIPTION",
					revision: workspace.revision,
					request,
				},
				{
					optimisticStep: "transcription-processing",
				},
			)
		},
		[performWorkflowAction, workspace],
	)

	const reopenTranscriptionConfig = useCallback(async () => {
		if (!workspace) return
		await performWorkflowAction(
			{
				type: "REOPEN_TRANSCRIPTION_CONFIG",
				revision: workspace.revision,
			},
			{
				optimisticStep: "transcription-settings",
			},
		)
	}, [performWorkflowAction, workspace])

	const markTranscriptionReviewed = useCallback(async () => {
		if (!workspace) return
		await performWorkflowAction(
			{
				type: "MARK_TRANSCRIPTION_REVIEWED",
				revision: workspace.revision,
			},
			{
				optimisticStep: "caption-settings",
			},
		)
	}, [performWorkflowAction, workspace])

	const selectCaptionPreset = useCallback(
		async (presetId: string | null) => {
			if (!workspace) return

			setCaptionPresetDraft(presetId)

			await performWorkflowAction({
				type: "SELECT_CAPTION_PRESET",
				revision: workspace.revision,
				preset_id: presetId,
			})
		},
		[performWorkflowAction, workspace],
	)

	const setCaptionPresetId = useCallback(
		async (id: string | null) => {
			await selectCaptionPreset(id)
		},
		[selectCaptionPreset],
	)

	const startCaptionRender = useCallback(async () => {
		if (!workspace) return

		await performWorkflowAction(
			{
				type: "START_CAPTION_RENDER",
				revision: workspace.revision,
			},
			{
				optimisticStep: "caption-processing",
			},
		)
	}, [performWorkflowAction, workspace])

	const reopenCaptionConfig = useCallback(async () => {
		if (!workspace) return

		await performWorkflowAction(
			{
				type: "REOPEN_CAPTION_CONFIG",
				revision: workspace.revision,
			},
			{
				optimisticStep: "caption-settings",
			},
		)
	}, [performWorkflowAction, workspace])

	const value = useMemo<WizardContextValue>(
		() => ({
			projectId,
			revision,
			currentStep,
			stepIndex: WIZARD_STEPS.findIndex((step) => step.key === currentStep),
			completedSteps,
			primaryFileId,
			primaryFileKey,
			videoUrl,
			originalFileName,
			silenceSettings: silenceSettingsDraft,
			activeJobId,
			activeJobType,
			silenceJobId,
			transcriptionArtifactId,
			captionPresetId,
			captionStyleConfig,
			captionedVideoPath,
			captionedVideoFileId,
			goToStep,
			goNext,
			goBack,
			setFileKey,
			setSilenceSettings,
			setActiveJob,
			startProcessingJob,
			setSilenceJobId,
			setTranscriptionArtifactId,
			setCaptionPresetId,
			setCaptionStyleConfig,
			setCaptionedVideoPath,
			setCaptionedVideoFileId,
			markStepCompleted,
			confirmVerify,
			startMediaConvert,
			saveSilenceSettings,
			startSilenceDetect,
			saveSilenceCuts,
			skipSilenceApply,
			startSilenceApply,
			reopenSilenceReview,
			startTranscription,
			reopenTranscriptionConfig,
			markTranscriptionReviewed,
			selectCaptionPreset,
			startCaptionRender,
			reopenCaptionConfig,
		}),
		[
			activeJobId,
			activeJobType,
			captionPresetId,
			captionStyleConfig,
			captionedVideoFileId,
			captionedVideoPath,
			completedSteps,
			confirmVerify,
			currentStep,
			goBack,
			goNext,
			goToStep,
			markStepCompleted,
			markTranscriptionReviewed,
			originalFileName,
			primaryFileId,
			primaryFileKey,
			projectId,
			reopenCaptionConfig,
			reopenSilenceReview,
			reopenTranscriptionConfig,
			revision,
			saveSilenceCuts,
			saveSilenceSettings,
			selectCaptionPreset,
			setActiveJob,
			setCaptionPresetId,
			setCaptionStyleConfig,
			setCaptionedVideoFileId,
			setCaptionedVideoPath,
			setFileKey,
			setSilenceJobId,
			setSilenceSettings,
			setTranscriptionArtifactId,
			silenceJobId,
			silenceSettingsDraft,
			skipSilenceApply,
			startCaptionRender,
			startMediaConvert,
			startProcessingJob,
			startSilenceApply,
			startSilenceDetect,
			startTranscription,
			transcriptionArtifactId,
			videoUrl,
		],
	)

	return (
		<WizardContext.Provider value={value}>{children}</WizardContext.Provider>
	)
}

export function useWizard(): WizardContextValue {
	const ctx = useContext(WizardContext)
	if (!ctx) {
		throw new Error("useWizard must be used within WizardProvider")
	}
	return ctx
}
