import { type ReactNode, useEffect, useState } from "react"
import { type InitCustomBlockOptions, NotInIframeError } from "../index.js"
import { DebugMessageLog } from "./DebugMessageLog.js"
import { seedStandalonePreviewState } from "./standalonePreview.js"
import { useCustomBlockAutoResize } from "./useCustomBlockAutoResize.js"
import { useCustomBlockInit } from "./useCustomBlockInit.js"
import { useCustomBlockHost } from "./useHostState.js"
import "./NotionCustomBlock.css"

/**
 * Props accepted by {@link NotionCustomBlock}.
 */
export type NotionCustomBlockProps = InitCustomBlockOptions & {
	children: ReactNode
	/**
	 * Rendered while the SDK ↔ host handshake is in progress. Defaults to
	 * `null` (nothing).
	 */
	fallback?: ReactNode
	/**
	 * Rendered when the handshake fails. Either a node, or a function that
	 * receives the `Error`. When omitted, a friendly default error card is
	 * rendered with developer details — replace it for production templates.
	 */
	errorFallback?: ReactNode | ((error: Error) => ReactNode)
	/**
	 * Whether the provider should automatically post resize messages so the
	 * host iframe matches the content height of `#root`. Defaults to `true`.
	 * Pass `false` when you want to use the default block size and are ok
	 * with scrollbars within the Notion client.
	 *
	 * @default true
	 */
	autoResize?: boolean
}

/**
 * Top-level wrapper that runs the SDK ↔ host handshake and gates `children`
 * until it resolves. Passes `timeoutMs` straight through to
 * {@link initCustomBlock}.
 *
 * Templates that prefer not to write a `Root` gating component (or top-level
 * `await`) can mount their app entirely inside this provider:
 *
 * ```tsx
 * ReactDOM.createRoot(root).render(
 *   <NotionCustomBlock>
 *     <App />
 *   </NotionCustomBlock>,
 * )
 * ```
 *
 * Inside `children`, every SDK hook is guaranteed to return a populated
 * value. Outside the provider (or during the loading window), they throw.
 */
export function NotionCustomBlock({
	children,
	timeoutMs,
	fallback = null,
	errorFallback,
	autoResize = true,
}: NotionCustomBlockProps) {
	const init = useCustomBlockInit({ timeoutMs })
	useCustomBlockAutoResize({ enabled: autoResize })
	const isStandalone = init.error instanceof NotInIframeError
	const host = useCustomBlockHost()
	const [debugOpen, setDebugOpen] = useState(false)

	useEffect(() => {
		const onKeyDown = (e: KeyboardEvent) => {
			if (e.key === "\\") {
				setDebugOpen(prev => !prev)
			}
		}
		window.addEventListener("keydown", onKeyDown)
		return () => window.removeEventListener("keydown", onKeyDown)
	}, [])

	useEffect(() => {
		if (!isStandalone) {
			return
		}
		console.warn(`[notion-custom-sdk] ${init.error?.message}`)
		seedStandalonePreviewState()
	}, [isStandalone, init.error])

	if (debugOpen) {
		return <DebugMessageLog />
	}
	if (init.error && !isStandalone) {
		if (errorFallback === undefined) {
			return <DefaultInitErrorFallback error={init.error} />
		}
		return (
			<>
				{typeof errorFallback === "function"
					? errorFallback(init.error)
					: errorFallback}
			</>
		)
	}
	if (isStandalone) {
		// Wait for the effect to seed placeholder host state; otherwise hooks
		// in `children` would throw.
		if (host.status !== "initialized") {
			return <>{fallback}</>
		}
		return (
			<>
				<div role="status" className="ncblock-standalone-banner">
					Notion host not detected — running in standalone preview. SDK hooks
					return placeholder values until embedded in Notion.
				</div>
				{children}
			</>
		)
	}
	if (!init.isLoaded) {
		return <>{fallback}</>
	}
	return <>{children}</>
}

// TODO(custom-blocks): Align the styling of this with the host-side error states.
function DefaultInitErrorFallback({ error }: { error: Error }) {
	useEffect(() => {
		console.warn(
			`[notion-custom-sdk] Custom block init failed: ${error.message}`,
		)
	}, [error])

	return (
		<div role="alert" className="ncblock-default-init-error">
			<div className="ncblock-default-init-error__card">
				<div className="ncblock-default-init-error__eyebrow">Custom block</div>
				<h2 className="ncblock-default-init-error__title">
					Couldn&apos;t connect to Notion
				</h2>
				<p className="ncblock-default-init-error__body">
					This custom block loaded, but the setup handshake didn&apos;t finish.
					Try again from Notion. If this keeps happening, check the browser
					console for SDK details.
				</p>
				<details className="ncblock-default-init-error__details">
					<summary>Developer details</summary>
					<pre className="ncblock-default-init-error__pre">{error.message}</pre>
				</details>
			</div>
		</div>
	)
}
