/**
 * E2E Test Fixtures
 *
 * Each fixture uses GENERATED mizan hooks (not raw call()).
 * Playwright reads the DOM to verify behavior.
 *
 * URL hash selects the fixture: #echo, #add, #multiply, etc.
 */

import { useState, useEffect, useRef } from 'react'

// Generated typed hooks — the actual mizan API
import {
    MizanContext,
    useEcho,
    useAdd,
    useMultiply,
    useWhoami,
    useStaffOnly,
    useSuperuserOnly,
    useVerifiedOnly,
    useNotImplementedFn,
    useBuggyFn,
    usePermissionCheckFn,
    useCurrentUser,
    MizanError,
    useMizan,
} from './api'
import { useChatChannel } from './api/channels.hooks'

// ─── Fixture router ─────────────────────────────────────────────────────────

export function Fixtures() {
    const [hash, setHash] = useState(window.location.hash.slice(1))

    useEffect(() => {
        const onHash = () => setHash(window.location.hash.slice(1))
        window.addEventListener('hashchange', onHash)
        return () => window.removeEventListener('hashchange', onHash)
    }, [])

    switch (hash) {
        case 'echo': return <Echo />
        case 'add': return <Add />
        case 'multiply': return <Multiply />
        case 'not-found': return <NotFound />
        case 'validation-error': return <ValidationError />
        case 'auth-required': return <AuthRequired />
        case 'staff-only': return <StaffOnly />
        case 'superuser-only': return <SuperuserOnly />
        case 'verified-only': return <VerifiedOnly />
        case 'not-implemented': return <NotImplemented />
        case 'internal-error': return <InternalError />
        case 'permission-error': return <PermissionError_ />
        case 'permission-success': return <PermissionSuccess />
        case 'context-current-user': return <ContextCurrentUser />
        // Form fixtures removed — forms codegen deferred per Blazr scope
        case 'channel-chat': return <ChannelChatFixture />
        default: return <div data-testid="ready">Harness ready. Set #hash.</div>
    }
}

// ─── Result helper ──────────────────────────────────────────────────────────

function Result({ data, error }: { data?: unknown; error?: unknown }) {
    return (
        <>
            {data !== undefined && (
                <pre data-testid="result">{JSON.stringify(data)}</pre>
            )}
            {error !== undefined && error !== null && (
                <>
                    <div data-testid="error-type">
                        {error instanceof MizanError ? 'MizanError' : 'Error'}
                    </div>
                    <div data-testid="error-code">
                        {error instanceof MizanError ? error.code : ''}
                    </div>
                    <pre data-testid="error-message">
                        {error instanceof Error ? error.message : String(error)}
                    </pre>
                </>
            )}
        </>
    )
}

// ─── Hook runner: calls a generated hook and renders result ─────────────────

function useRun<T>(hook: () => { mutate: (input?: any) => Promise<T> }, input?: any) {
    const { mutate } = hook()
    const [data, setData] = useState<T>()
    const [error, setError] = useState<unknown>()

    useEffect(() => {
        mutate(input).then(setData).catch(setError)
    }, []) // eslint-disable-line react-hooks/exhaustive-deps

    return { data, error }
}

// ─── Server function fixtures ───────────────────────────────────────────────

function Echo() {
    const { data, error } = useRun(useEcho, { text: 'e2e-test' })
    return <Result data={data} error={error} />
}

function Add() {
    const { data, error } = useRun(useAdd, { a: 17, b: 25 })
    return <Result data={data} error={error} />
}

function Multiply() {
    const { data, error } = useRun(useMultiply, { x: 6, y: 7 })
    return <Result data={data} error={error} />
}

function NotFound() {
    // Deliberately call a non-existent function via the raw primitive
    const { call } = useMizan()
    const [error, setError] = useState<unknown>()
    useEffect(() => { call('does_not_exist').catch(setError) }, [call])
    return <Result error={error} />
}

function ValidationError() {
    // Send wrong types to add (strings instead of numbers)
    const { mutate } = useAdd()
    const [error, setError] = useState<unknown>()
    useEffect(() => { (mutate as any)({ a: 'not_a_number', b: 'also_not' }).catch(setError) }, [mutate])
    return <Result error={error} />
}

function AuthRequired() {
    const { data, error } = useRun(useWhoami)
    return <Result data={data} error={error} />
}

function StaffOnly() {
    const { data, error } = useRun(useStaffOnly)
    return <Result data={data} error={error} />
}

function SuperuserOnly() {
    const { data, error } = useRun(useSuperuserOnly)
    return <Result data={data} error={error} />
}

function VerifiedOnly() {
    const { data, error } = useRun(useVerifiedOnly)
    return <Result data={data} error={error} />
}

function NotImplemented() {
    const { data, error } = useRun(useNotImplementedFn)
    return <Result data={data} error={error} />
}

function InternalError() {
    const { data, error } = useRun(useBuggyFn)
    return <Result data={data} error={error} />
}

function PermissionError_() {
    const { data, error } = useRun(usePermissionCheckFn, { secret: 'wrong' })
    return <Result data={data} error={error} />
}

function PermissionSuccess() {
    const { data, error } = useRun(usePermissionCheckFn, { secret: 'open-sesame' })
    return <Result data={data} error={error} />
}

// ─── Context fixtures ───────────────────────────────────────────────────────

function ContextCurrentUser() {
    // useCurrentUser throws if context not loaded yet, so catch that
    try {
        const user = useCurrentUser()
        return <pre data-testid="result">{JSON.stringify(user)}</pre>
    } catch {
        return <div>loading context...</div>
    }
}

// ─── Channel fixtures ───────────────────────────────────────────────────────

function ChannelChatFixture() {
    // MizanContext already includes ChannelProvider
    return <ChannelChat />
}

function ChannelChat() {
    const chat = useChatChannel({ room: 'e2e' })
    const [sent, setSent] = useState(false)
    const prevStatus = useRef(chat.status)

    useEffect(() => {
        // Send once when status transitions to 'connected' (meaning subscribed)
        // The hook maps subscribed → 'connected', but we need to wait for it
        // to go through 'connecting' first (before subscription is confirmed)
        const wasConnecting = prevStatus.current === 'connecting'
        prevStatus.current = chat.status

        if (wasConnecting && chat.status === 'connected' && !sent) {
            chat.send({ text: 'hello from e2e' })
            setSent(true)
        }
    }, [chat.status, sent, chat])

    return (
        <div>
            <div data-testid="channel-status">{chat.status}</div>
            <div data-testid="channel-message-count">{chat.messages.length}</div>
            {chat.messages.length > 0 && (
                <pre data-testid="channel-last-message">
                    {JSON.stringify(chat.messages[chat.messages.length - 1])}
                </pre>
            )}
        </div>
    )
}
