'use client';

/**
 * @author: Kim Jino
 * @since: 2025.03.31
 * @description: CKEditor component for Next.js using Script component for loading CKEditor script
 */

import '@/styles/editor.scss';
import Script from 'next/script';
import React, { useRef, useState, useEffect, useCallback, useMemo, forwardRef, useImperativeHandle } from 'react';
import { CKEditor } from '@ckeditor/ckeditor5-react';
import type { Editor as CKEditorInstance } from 'ckeditor5';
import Loading from '@/app/component/Loading';
import BoardMeta from '@/types/forum/boardMeta';
import { PostConst } from '@/constants/forum';

export interface Handle {
	editorInstance: CKEditorInstance|null;
	getFileStore(): UploadedFile[];
	getImageStore(): UploadedImage[];
	getMediaStore(): UploadedMedia[];
}

interface Props {
	editorKey: string;
	data: string;
	onChange: (data: string) => void;
	boardMeta?: BoardMeta|null;
}

const Editor = forwardRef<Handle, Props>(({ editorKey = '', data = '', onChange, boardMeta }: Props, ref) =>
{
	const [ready, setReady] = useState<boolean>(false); // CKEditor가 준비되었는지 여부
	const [instanceLoaded, setInstanceLoaded] = useState<boolean>(false);
	const editorRef = useRef<typeof window.Editor|null>(null); // CKEditor 객체
	const editorInstanceRef = useRef<CKEditorInstance|null>(null);

	useEffect(() => {
		if (typeof window !== 'undefined' && window.Editor) {
			editorRef.current = window.Editor;
			setReady(true);
		}
	}, [editorKey]); // 게시판 주소가 바뀌면 다시 ready 확인

	const handleScriptLoad = () => {
		if (typeof window !== 'undefined' && window.Editor) {
			editorRef.current = window.Editor;
			setReady(true);
		} else {
			console.error('CKEditor script not loaded properly.');
		}
	};

	// 외부에 접근 가능한 명령어
	useImperativeHandle(ref, () => ({
		get editorInstance() { return editorInstanceRef.current; },
		getFileStore: () => editorInstanceRef.current?._fileStore || [],
		getImageStore: () => editorInstanceRef.current?._imageStore || [],
		getMediaStore: () => editorInstanceRef.current?._mediaStore || []
	}), [instanceLoaded]);

	const editorConfig = useMemo(() => ({
		placeholder: '내용을 입력하세요.',
		maxWordCount: PostConst.MaxAllowedContentLength, // 최대 본문 길이

		// 이미지 설정
		allowImage: boardMeta?.write.allowImage ?? false,
		imageUploadLimit: boardMeta?.write.imageUploadLimit,
		imageUploadMaxSize: boardMeta?.write.imageUploadMaxSize,

		// 동영상 설정
		allowMedia: boardMeta?.write.allowMedia ?? false,
		mediaUploadLimit: boardMeta?.write.mediaUploadLimit,

		// 파일 설정
		allowFile: boardMeta?.write.allowFile ?? false,
		fileUploadLimit: boardMeta?.write.fileUploadLimit,
		fileUploadMaxSize: boardMeta?.write.fileUploadMaxSize,
		fileUploadExtension: boardMeta?.write.fileUploadExtension

		/*
		// 글자수 세기
		wordCount: {
			onUpdate: (stats: { characters: number; words: number }) => {
				document.getElementById('editorTxtLength')!.innerText = stats.characters.toString();
			}
		}
		*/
	}), []);

	const handleOnReady = useCallback((editor: CKEditorInstance) => {
		console.log('Editor was initialized');

		// 최소 높이 설정
		editor.editing.view.change(writer => {
			const root = editor.editing.view.document.getRoot();
			if (root) {
				writer.setStyle('min-height', '300px', root);
			}
		});

		editorInstanceRef.current = editor;
		setInstanceLoaded(true);

		// 외부 내용을 복사 붙여넣기 가능하도록 처리
		editor.editing.view.document.on('paste', (_, data) => {
			const html = data.dataTransfer.getData('text/html');
			const text = data.dataTransfer.getData('text/plain');
			const content = (html || text);

			if (content) {
				const viewFragment = editor.data.processor.toView(content);
				const modelFragment = editor.data.toModel(viewFragment);

				editor.model.change(() => {
					editor.model.insertContent(modelFragment);
				});
			}
		});
	}, []);

	const handleChange = useCallback((_: unknown, editor: CKEditorInstance) => {
		onChange(editor.getData()); // 불필요한 optional chaining 제거
	}, [onChange]);

	return (
		<>
			<Script src="/editor/editor.min.js" strategy="afterInteractive" onLoad={handleScriptLoad}/>

			{ready && editorRef.current ? (
				<CKEditor
					editor={editorRef.current}
					data={data}
					config={editorConfig}
					onReady={handleOnReady}
					onChange={handleChange}
				/>
			) : (
				<Loading/>
			)}
		</>
	);
});

Editor.displayName = 'Editor';

export default Editor;