"use client"

import {
	createContext,
	FunctionComponent,
	ReactNode,
	useCallback,
	useContext,
	useEffect,
	useMemo,
	useRef,
	useState,
} from "react"

import api from "@shared/api"
import {
	type WorkflowWorkspaceView,
	useProjectWorkspaceQuery,
	useWorkflowAction,
} from "@shared/api/projectWorkflow"
import { useDebounce } from "@shared/hooks/useDebounce"

export type SelectedFile = {
	id: string
	path: string
	source: "file" | "artifact" | "media"
	artifactType?: string
	mimeType?: string
	scrollToSegmentIndex?: number
}

export type UsedFile = SelectedFile & {
	displayName: string
	iconType: "video" | "audio" | "text" | "other"
}

interface WorkspaceFileContextValue {
	selectedFile: SelectedFile | null
	setSelectedFile: (file: SelectedFile | null) => void
	usedFiles: UsedFile[]
	addUsedFile: (file: UsedFile) => void
	removeUsedFile: (id: string) => void
	isFileUsed: (id: string) => boolean
	isLoaded: boolean
}

const FileContext = createContext<WorkspaceFileContextValue | null>(null)

const DEBOUNCE_MS = 300

function getFileIconType(mimeType: string | null | undefined) {
	if (!mimeType) return "other" as const
	if (mimeType.startsWith("video/")) return "video" as const
	if (mimeType.startsWith("audio/")) return "audio" as const
	if (mimeType.includes("json") || mimeType.startsWith("text/")) {
		return "text" as const
	}
	return "other" as const
}

function getArtifactDisplayName(artifactType: string | null | undefined): string {
	switch (artifactType) {
		case "TRANSCRIPTION_JSON":
			return "Субтитры"
		default:
			return artifactType ?? "Артефакт"
	}
}

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

	const [usedFileIds, setUsedFileIds] = useState<string[]>([])
	const [selectedPersistedId, setSelectedPersistedId] = useState<string | null>(
		null,
	)
	const [selectedFile, setSelectedFileState] = useState<SelectedFile | null>(null)
	const latestRevisionRef = useRef<number | null>(null)

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

		if (latestRevisionRef.current === workspace.revision) {
			return
		}

		latestRevisionRef.current = workspace.revision
		setUsedFileIds(workspace.workspace_view.used_file_ids)
		setSelectedPersistedId(workspace.workspace_view.selected_file_id)
	}, [workspace])

	const { data: files } = api.useQuery("get", "/api/files/files/", {})
	const { data: artifacts } = api.useQuery("get", "/api/media/artifacts/", {})

	const fileMap = useMemo(() => {
		const nextMap = new Map<string, UsedFile>()

		for (const file of files ?? []) {
			if (file.project_id !== projectId || file.is_deleted) continue

			nextMap.set(file.id, {
				id: file.id,
				path: file.path,
				source: "file",
				mimeType: file.mime_type,
				displayName: file.original_filename,
				iconType: getFileIconType(file.mime_type),
			})
		}

		return nextMap
	}, [files, projectId])

	const artifactMap = useMemo(() => {
		const nextMap = new Map<string, UsedFile>()

		for (const artifact of artifacts ?? []) {
			if (artifact.project_id !== projectId || artifact.is_deleted) continue

			nextMap.set(artifact.id, {
				id: artifact.id,
				path: "transcription",
				source: "artifact",
				artifactType: artifact.artifact_type,
				displayName: getArtifactDisplayName(artifact.artifact_type),
				iconType:
					artifact.artifact_type === "TRANSCRIPTION_JSON" ? "text" : "other",
			})
		}

		return nextMap
	}, [artifacts, projectId])

	const resolveUsedFile = useCallback(
		(fileId: string, previous?: UsedFile | null): UsedFile | null => {
			return fileMap.get(fileId) ?? artifactMap.get(fileId) ?? previous ?? null
		},
		[fileMap, artifactMap],
	)

	const usedFiles = useMemo(
		() =>
			usedFileIds
				.map((fileId) => resolveUsedFile(fileId))
				.filter((file): file is UsedFile => file !== null),
		[resolveUsedFile, usedFileIds],
	)

	useEffect(() => {
		setSelectedFileState((prev) => {
			if (!selectedPersistedId) return null

			const resolved = resolveUsedFile(
				selectedPersistedId,
				prev as UsedFile | null,
			)
			if (!resolved) return prev

			if (prev?.id === selectedPersistedId) {
				return {
					...resolved,
					scrollToSegmentIndex: prev.scrollToSegmentIndex,
				}
			}

			return resolved
		})
	}, [resolveUsedFile, selectedPersistedId])

	const persistableWorkspaceView = useMemo<WorkflowWorkspaceView>(
		() => ({
			used_file_ids: usedFileIds,
			selected_file_id: selectedPersistedId,
		}),
		[selectedPersistedId, usedFileIds],
	)

	const debouncedWorkspaceView = useDebounce(
		persistableWorkspaceView,
		DEBOUNCE_MS,
	)

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

		const localSignature = JSON.stringify(debouncedWorkspaceView)
		const serverSignature = JSON.stringify(workspace.workspace_view)

		if (localSignature === serverSignature) return

		void workflowAction.mutateAsync({
			type: "SET_WORKSPACE_VIEW",
			revision: workspace.revision,
			workspace_view: debouncedWorkspaceView,
		})
	}, [debouncedWorkspaceView, workflowAction, workspace])

	const setSelectedFile = useCallback((file: SelectedFile | null) => {
		setSelectedFileState(file)
		setSelectedPersistedId(file?.id ?? null)
	}, [])

	const addUsedFile = useCallback((file: UsedFile) => {
		setUsedFileIds((prev) => {
			if (prev.includes(file.id)) return prev
			return [...prev, file.id]
		})
	}, [])

	const removeUsedFile = useCallback((id: string) => {
		setUsedFileIds((prev) => prev.filter((fileId) => fileId !== id))
		setSelectedPersistedId((prev) => (prev === id ? null : prev))
		setSelectedFileState((prev) => (prev?.id === id ? null : prev))
	}, [])

	const isFileUsed = useCallback(
		(id: string) => usedFileIds.includes(id),
		[usedFileIds],
	)

	const value = useMemo<WorkspaceFileContextValue>(
		() => ({
			selectedFile,
			setSelectedFile,
			usedFiles,
			addUsedFile,
			removeUsedFile,
			isFileUsed,
			isLoaded: Boolean(workspace),
		}),
		[
			addUsedFile,
			isFileUsed,
			removeUsedFile,
			selectedFile,
			setSelectedFile,
			usedFiles,
			workspace,
		],
	)

	return <FileContext.Provider value={value}>{children}</FileContext.Provider>
}

export function useWorkspaceFiles(): WorkspaceFileContextValue {
	const ctx = useContext(FileContext)
	if (!ctx) {
		throw new Error("useWorkspaceFiles must be used within WorkspaceProvider")
	}
	return ctx
}
