import { useEffect, useState, useRef } from 'react';
import { Column } from 'primereact/column';
import { FilterMatchMode } from 'primereact/api';
import TableGroupHeader from '../../shared/components/_V1/TableGroupHeader';
import TableGroup from '../../shared/components/_V1/TableGroup';
import { TSizeOptionValue } from './DataTable.types';
import { api } from '../../services/api';
import { Button } from 'primereact/button';
import { Dialog } from 'primereact/dialog';
import { InputText } from 'primereact/inputtext';
import { Dropdown } from 'primereact/dropdown';
import { ProgressSpinner } from 'primereact/progressspinner';
import ChatGptModal from './ChatGPTModal';
import { Toast } from 'primereact/toast';
import { useAuthStore } from '../../state/stores/useAuthStore';
import SettingsMenu, { SettingsMenuItem } from '../../shared/components/SettingsMenu';
import ManageDocsModal from './ManageDocsModal';
import PromptCreatorModal from './PromptCreatorModal';
import { Tooltip } from 'primereact/tooltip';

const CANVAS_APP_ID = 1; // Hardcoded Canvas app ID
const CANVAS_API_BASE_URL = import.meta.env.VITE_CANVAS_API_BASE_URL || 'https://canvas.instructure.com/api/v1';

export default function CanvasEndpoints() {
    const [endpoints, setEndpoints] = useState<any[]>([]);
    const [filters, setFilters] = useState<any>(null);
    const [globalFilterValue, setGlobalFilterValue] = useState('');
    const [showCreateModal, setShowCreateModal] = useState(false);
    const [newEndpoint, setNewEndpoint] = useState({ name: '', method: '', path: '', description: '', publicPath: '' });
    const sizeOptions: { label: string; value: TSizeOptionValue }[] = [
        { label: 'Small', value: 'small' },
        { label: 'Normal', value: 'normal' },
        { label: 'Large', value: 'large' },
    ];
    const [size, setSize] = useState<TSizeOptionValue>(sizeOptions[0].value);
    const [showCallModal, setShowCallModal] = useState(false);
    const [callModalData, setCallModalData] = useState<any[]>([]);
    const [callModalColumns, setCallModalColumns] = useState<string[]>([]);
    const [callModalLoading, setCallModalLoading] = useState(false);
    const [callEndpoint, setCallEndpoint] = useState<any>(null);
    const [callModalFilters, setCallModalFilters] = useState<any>(null);
    const [callModalGlobalFilterValue, setCallModalGlobalFilterValue] = useState('');
    const [callModalSize, setCallModalSize] = useState<TSizeOptionValue>(sizeOptions[0].value);
    const [callModalFirst, setCallModalFirst] = useState(0);
    const [callModalRows, setCallModalRows] = useState(10);
    const [showChatGptModal, setShowChatGptModal] = useState(false);
    const [chatPrompt, setChatPrompt] = useState('');
    const [chatMessages, setChatMessages] = useState<{ role: 'user' | 'assistant'; content: string }[]>([]);
    const [chatLoading, setChatLoading] = useState(false);
    const [chatError, setChatError] = useState<string | null>(null);
    const toast = useRef(null);
    const [editEndpointId, setEditEndpointId] = useState<number | null>(null);
    const [apiKey, setApiKey] = useState<string | null>(null);
    const [apiKeyLoading, setApiKeyLoading] = useState(false);
    const [apiKeyError, setApiKeyError] = useState<string | null>(null);
    const [appId, setAppId] = useState<number | null>(CANVAS_APP_ID);
    const [showApiKeyModal, setShowApiKeyModal] = useState(false);
    const [editByVoiceEndpoint, setEditByVoiceEndpoint] = useState<any>(null);
    const [showVoiceModal, setShowVoiceModal] = useState(false);
    const [liveTranscript, setLiveTranscript] = useState('');
    const [showVoiceInfoModal, setShowVoiceInfoModal] = useState(false);
    const [fullTranscript, setFullTranscript] = useState('');
    const [showCogMenu, setShowCogMenu] = useState(false);
    const cogMenuRef = useRef<any>(null);
    const [showManageDocs, setShowManageDocs] = useState(false);
    const [showPromptCreator, setShowPromptCreator] = useState(false);

    useEffect(() => {
        // Load API key from localStorage if available
        const storedKey = localStorage.getItem('canvas_api_key');
        if (storedKey) setApiKey(storedKey);
        initFilters();
        fetchEndpoints();
    }, []);

    const fetchEndpoints = async () => {
        try {
            const response = await api('get', '/api/v1/canvas-api/endpoints');
            setEndpoints(response.data);
        } catch (error) {
            console.error('Failed to fetch endpoints:', error);
        }
    };

    const initFilters = () => {
        setFilters({
            global: { value: null, matchMode: FilterMatchMode.CONTAINS },
        });
        setGlobalFilterValue('');
    };

    const clearFilter = () => {
        initFilters();
    };

    const onGlobalFilterChange = (e: { target: { value: any } }) => {
        const value = e.target.value;
        let _filters = { ...filters };
        _filters['global'].value = value;
        setFilters(_filters);
        setGlobalFilterValue(value);
    };

    // Utility to ensure path never starts with /api/v1
    function sanitizeCanvasPath(path: string) {
        return path.replace(/^\/api\/v1/, '');
    }

    const handleCreateEndpoint = async () => {
        try {
            const sanitizedEndpoint = { ...newEndpoint, path: sanitizeCanvasPath(newEndpoint.path) };
            await api('post', '/api/v1/canvas-api/endpoints', sanitizedEndpoint);
            setShowCreateModal(false);
            setNewEndpoint({ name: '', method: '', path: '', description: '', publicPath: '' });
            fetchEndpoints();
        } catch (error) {
            console.error('Failed to create endpoint:', error);
        }
    };

    const handleCallEndpoint = async (endpoint: any) => {
        setShowCallModal(true);
        setCallModalLoading(true);
        setCallEndpoint(endpoint);
        try {
            const res = await api('post', '/api/v1/canvas-api/proxy-external', {
                path: endpoint.path,
                method: endpoint.method || 'GET',
            });
            let data = res.data;
            if (!Array.isArray(data)) data = [data];
            setCallModalData(data);
            setCallModalColumns(data.length > 0 ? Object.keys(data[0]) : []);
        } catch (error) {
            setCallModalData([]);
            setCallModalColumns([]);
        } finally {
            setCallModalLoading(false);
        }
    };

    const initCallModalFilters = () => {
        setCallModalFilters({
            global: { value: null, matchMode: FilterMatchMode.CONTAINS },
        });
        setCallModalGlobalFilterValue('');
    };

    const clearCallModalFilter = () => {
        initCallModalFilters();
    };

    const onCallModalGlobalFilterChange = (e: { target: { value: any } }) => {
        const value = e.target.value;
        let _filters = { ...callModalFilters };
        _filters['global'].value = value;
        setCallModalFilters(_filters);
        setCallModalGlobalFilterValue(value);
    };

    const handleChatPromptSend = async (promptOverride?: string) => {
        const prompt = promptOverride !== undefined ? promptOverride : chatPrompt;
        if (!prompt.trim()) return;
        setChatMessages(prev => [...prev, { role: 'user', content: prompt }]);
        setChatLoading(true);
        setChatError(null);
        try {
            const formattedRequest = { data: prompt };
            const response = await api(
                'post',
                '/api/v1/canvas-api/chatgpt/completions',
                formattedRequest,
                undefined,
                'json',
                60,
                true
            );
            const data = response.data;
            let isEndpointJson = false;
            if (data && data.responseText) {
                let parsed;
                try {
                    parsed = typeof data.responseText === 'string' ? JSON.parse(data.responseText) : data.responseText;
                } catch (e) {
                    parsed = null;
                }
                if (parsed && parsed.name && parsed.method && parsed.path) {
                    // Auto-create endpoint
                    const sanitizedParsed = { ...parsed, path: sanitizeCanvasPath(parsed.path) };
                    await api('post', '/api/v1/canvas-api/endpoints', sanitizedParsed);
                    fetchEndpoints();
                    isEndpointJson = true;
                    if (toast.current) {
                        toast.current.show({ severity: 'success', summary: 'Endpoint Created', detail: `Endpoint "${parsed.name}" created successfully!` });
                    }
                }
            }
            if (!isEndpointJson) {
                setChatMessages(prev => [...prev, { role: 'assistant', content: typeof data.responseText === 'string' ? data.responseText : JSON.stringify(data.responseText, null, 2) }]);
            }
        } catch (err) {
            setChatError('Failed to get response from server.');
            setChatMessages(prev => [...prev, { role: 'assistant', content: 'Failed to get response from server.' }]);
        } finally {
            setChatLoading(false);
            setChatPrompt('');
        }
    };

    const handleChatPromptKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
        if (e.key === 'Enter' && !e.shiftKey) {
            e.preventDefault();
            handleChatPromptSend();
        }
    };

    const handleDeleteEndpoint = async (endpoint: any) => {
        try {
            await api('delete', `/api/v1/canvas-api/endpoints/${endpoint.id}`);
            fetchEndpoints();
            if (toast.current) {
                toast.current.show({ severity: 'success', summary: 'Endpoint Deleted', detail: `Endpoint "${endpoint.name}" deleted successfully!` });
            }
        } catch (error) {
            if (toast.current) {
                toast.current.show({ severity: 'error', summary: 'Delete Failed', detail: 'Failed to delete endpoint.' });
            }
        }
    };

    const openEditModal = (endpoint: any) => {
        setEditEndpointId(endpoint.id);
        setNewEndpoint({
            name: endpoint.name,
            method: endpoint.method,
            path: endpoint.path,
            description: endpoint.description || '',
            publicPath: endpoint.publicPath || '',
        });
        setShowCreateModal(true);
    };

    const handleSaveEndpoint = async () => {
        try {
            const sanitizedEndpoint = { ...newEndpoint, path: sanitizeCanvasPath(newEndpoint.path) };
            if (editEndpointId) {
                await api('put', `/api/v1/canvas-api/endpoints/${editEndpointId}`, sanitizedEndpoint);
            } else {
                await api('post', '/api/v1/canvas-api/endpoints', sanitizedEndpoint);
            }
            setShowCreateModal(false);
            setNewEndpoint({ name: '', method: '', path: '', description: '', publicPath: '' });
            setEditEndpointId(null);
            fetchEndpoints();
        } catch (error) {
            console.error('Failed to save endpoint:', error);
        }
    };

    const handleModalHide = () => {
        setShowCreateModal(false);
        setNewEndpoint({ name: '', method: '', path: '', description: '', publicPath: '' });
        setEditEndpointId(null);
    };

    const handleApiKey = async () => {
        console.log('handleApiKey called');
        setApiKeyLoading(true);
        setApiKeyError(null);
        try {
            const user = useAuthStore.getState().user;
            console.log('User:', user, 'AppId:', appId);
            if (!user) throw new Error('User not logged in');
            if (!appId) throw new Error('App ID not loaded');
            const response = await api('post', '/api/v1/app/apikey/generate', { userId: user.id, appId });
            setApiKey(response.data.apiKey);
            localStorage.setItem('canvas_api_key', response.data.apiKey);
        } catch (error: any) {
            const errorMessage = error.response?.data?.error || error.message || 'Failed to fetch or generate API key';
            setApiKeyError(errorMessage);
            console.error('API Key Error:', error);
        } finally {
            setApiKeyLoading(false);
        }
    };

    const fetchApiKey = async () => {
        try {
            const user = useAuthStore.getState().user;
            if (!user || !appId) return;
            const response = await api('post', '/api/v1/app/apikey/get', { userId: user.id, appId });
            setApiKey(response.data.apiKey || null);
            if (response.data.apiKey) {
                localStorage.setItem('canvas_api_key', response.data.apiKey);
            }
            setApiKeyError(null);
        } catch (error: any) {
            setApiKey(null);
            setApiKeyError(error.response?.data?.error || error.message || 'Failed to fetch API key');
        }
    };

    useEffect(() => {
        if (showApiKeyModal) {
            fetchApiKey();
        }
        // eslint-disable-next-line
    }, [showApiKeyModal, appId]);

    const tryDeleteFromSpeech = async (transcript) => {
        const lower = transcript.toLowerCase();
        if (lower.startsWith('create')) return false; // let normal create/chat logic handle it

        if (lower.includes('delete')) {
            // Try to match "delete endpoint 5" or "remove endpoint 5"
            const idMatch = transcript.match(/delete endpoint (\d+)/i) || transcript.match(/remove endpoint (\d+)/i);
            if (idMatch) {
                const id = parseInt(idMatch[1], 10);
                await handleDeleteEndpoint({ id });
                setChatMessages(prev => [...prev, { role: 'assistant', content: `Endpoint with ID ${id} deleted (if it existed).` }]);
                return true;
            }
            // Try to match "delete endpoint called X" or "remove endpoint named X"
            const nameMatch = transcript.match(/delete endpoint (called|named) (.+)/i) || transcript.match(/remove endpoint (called|named) (.+)/i);
            if (nameMatch) {
                const name = nameMatch[2].trim();
                const endpoint = endpoints.find(e => e.name.toLowerCase() === name.toLowerCase());
                if (endpoint) {
                    await handleDeleteEndpoint(endpoint);
                    setChatMessages(prev => [...prev, { role: 'assistant', content: `Endpoint \"${name}\" deleted.` }]);
                } else {
                    setChatMessages(prev => [...prev, { role: 'assistant', content: `No endpoint found with name \"${name}\".` }]);
                }
                return true;
            }
            // Try to match "delete endpoint [name]"
            const nameAfterEndpointMatch = transcript.match(/delete endpoint ([a-zA-Z0-9 _-]+)/i);
            if (nameAfterEndpointMatch) {
                const name = nameAfterEndpointMatch[1].trim();
                const endpoint = endpoints.find(e => e.name.toLowerCase() === name.toLowerCase());
                if (endpoint) {
                    await handleDeleteEndpoint(endpoint);
                    setChatMessages(prev => [...prev, { role: 'assistant', content: `Endpoint \"${name}\" deleted.` }]);
                } else {
                    setChatMessages(prev => [...prev, { role: 'assistant', content: `No endpoint found with name \"${name}\".` }]);
                }
                return true;
            }
            // If just "delete endpoint" with no id or name, show a message
            if (lower.includes('delete endpoint')) {
                setChatMessages(prev => [...prev, { role: 'assistant', content: `Please specify the endpoint ID or name to delete.` }]);
                return true;
            }
        }
        return false;
    };

    const tryShowFromSpeech = async (transcript) => {
        const lower = transcript.toLowerCase();
        const viewKeywords = ['show', 'view', 'open'];
        // Find which keyword (if any) is present
        const action = viewKeywords.find(kw => lower.includes(kw));
        if (!action) return false;

        // Try "<action> endpoint 5"
        const idMatch = lower.match(/(?:show|view|open) endpoint (\d+)/i);
        if (idMatch) {
            const id = parseInt(idMatch[1], 10);
            const endpoint = endpoints.find(e => e.id === id);
            if (endpoint) {
                handleCallEndpoint(endpoint);
                setChatMessages(prev => [...prev, { role: 'assistant', content: `${action.charAt(0).toUpperCase() + action.slice(1)}ing endpoint with ID ${id}.` }]);
            } else {
                setChatMessages(prev => [...prev, { role: 'assistant', content: `No endpoint found with ID ${id}.` }]);
            }
            return true;
        }

        // Try "<action> endpoint [name]"
        const nameAfterEndpointMatch = lower.match(/(?:show|view|open) endpoint ([a-zA-Z0-9 _-]+)/i);
        if (nameAfterEndpointMatch) {
            const name = nameAfterEndpointMatch[1].trim();
            const endpoint = endpoints.find(e => e.name.toLowerCase() === name.toLowerCase());
            if (endpoint) {
                handleCallEndpoint(endpoint);
                setChatMessages(prev => [...prev, { role: 'assistant', content: `${action.charAt(0).toUpperCase() + action.slice(1)}ing endpoint \"${name}\".` }]);
            } else {
                setChatMessages(prev => [...prev, { role: 'assistant', content: `No endpoint found with name \"${name}\".` }]);
            }
            return true;
        }

        // Try "<action> [name] endpoint"
        const nameEndpointMatch = lower.match(/(?:show|view|open) (.+) endpoint/i);
        if (nameEndpointMatch) {
            const name = nameEndpointMatch[1].trim();
            const endpoint = endpoints.find(e => e.name.toLowerCase() === name.toLowerCase());
            if (endpoint) {
                handleCallEndpoint(endpoint);
                setChatMessages(prev => [...prev, { role: 'assistant', content: `${action.charAt(0).toUpperCase() + action.slice(1)}ing endpoint \"${name}\".` }]);
            } else {
                setChatMessages(prev => [...prev, { role: 'assistant', content: `No endpoint found with name \"${name}\".` }]);
            }
            return true;
        }

        // Try "<action> [name]"
        const nameMatch = lower.match(/(?:show|view|open) (.+)/i);
        if (nameMatch) {
            const name = nameMatch[1].trim();
            const endpoint = endpoints.find(e => e.name.toLowerCase() === name.toLowerCase());
            if (endpoint) {
                handleCallEndpoint(endpoint);
                setChatMessages(prev => [...prev, { role: 'assistant', content: `${action.charAt(0).toUpperCase() + action.slice(1)}ing endpoint \"${name}\".` }]);
            } else {
                setChatMessages(prev => [...prev, { role: 'assistant', content: `No endpoint found with name \"${name}\".` }]);
            }
            return true;
        }

        return false;
    };

    const trySearchFromSpeech = (transcript) => {
        const lower = transcript.toLowerCase().trim();
        // Match "search [term]", "find [term]", or "filter by [term]"
        const searchMatch = lower.match(/^(search|find|filter by)\s+(.+)$/i);
        if (searchMatch) {
            const term = searchMatch[2].trim();
            setGlobalFilterValue(term);
            setChatMessages(prev => [...prev, { role: 'assistant', content: `Filtered endpoints by: "${term}"` }]);
            return true;
        }
        return false;
    };

    const tryModifyFromSpeech = (transcript) => {
        const lower = transcript.toLowerCase();
        const match = lower.match(/modify endpoint (\d+)/i);
        if (match) {
            const id = parseInt(match[1], 10);
            const endpoint = endpoints.find(e => e.id === id);
            if (endpoint) {
                setEditByVoiceEndpoint({ ...endpoint });
                setChatMessages(prev => [...prev, { role: 'assistant', content: `Ready to modify endpoint with ID ${id}. Say 'method is GET' or 'path is slash course slash id'.` }]);
            } else {
                setChatMessages(prev => [...prev, { role: 'assistant', content: `No endpoint found with ID ${id}.` }]);
            }
            return true;
        }
        return false;
    };

    const tryUpdateEditByVoice = async (transcript) => {
        if (!editByVoiceEndpoint) return false;
        let updated = { ...editByVoiceEndpoint };
        let didUpdate = false;

        // Method update
        const methodMatch = transcript.match(/method is (get|post|put|delete|patch)/i);
        if (methodMatch) {
            updated.method = methodMatch[1].toUpperCase();
            didUpdate = true;
        }

        // Path update
        const pathMatch = transcript.match(/path is (.+)/i);
        if (pathMatch) {
            let path = pathMatch[1]
                .replace(/slash/gi, '/')
                .replace(/colon id|id/gi, ':id')
                .replace(/\s+/g, '');
            updated.path = path;
            didUpdate = true;
        }

        if (didUpdate) {
            setEditByVoiceEndpoint(updated);
            setChatMessages(prev => [...prev, { role: 'assistant', content: `Updated endpoint: method=${updated.method}, path=${updated.path}` }]);
            // Auto-save
            await api('put', `/api/v1/canvas-api/endpoints/${updated.id}`, updated);
            fetchEndpoints();
            setEditByVoiceEndpoint(null);
            setChatMessages(prev => [...prev, { role: 'assistant', content: `Endpoint ${updated.id} saved.` }]);
            return true;
        }
        return false;
    };

    const handleVoiceInput = () => {
        // @ts-ignore
        const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
        if (!SpeechRecognition) {
            alert('Speech recognition not supported in this browser.');
            return;
        }
        setShowVoiceModal(true);
        setLiveTranscript('');
        setFullTranscript('');
        const recognition = new SpeechRecognition();
        recognition.lang = 'en-US';
        recognition.interimResults = true;
        recognition.maxAlternatives = 1;

        recognition.onresult = async (event: any) => {
            const transcript = event.results[0][0].transcript;
            setLiveTranscript(transcript);
            setFullTranscript(prev => prev + (prev && !prev.endsWith(' ') ? ' ' : '') + transcript);
            setChatPrompt(transcript);
            if (event.results[0].isFinal) {
                setShowVoiceModal(false);
                setLiveTranscript('');
                setFullTranscript('');
                if (transcript.toLowerCase().includes('open command list')) {
                    setShowVoiceInfoModal(true);
                    return;
                }
                if (await tryDeleteFromSpeech(transcript)) return;
                if (await tryShowFromSpeech(transcript)) return;
                if (trySearchFromSpeech(transcript)) return;
                if (tryModifyFromSpeech(transcript)) return;
                if (await tryUpdateEditByVoice(transcript)) return;
                handleChatPromptSend(transcript);
            }
        };
        recognition.onerror = (event: any) => {
            setShowVoiceModal(false);
            setLiveTranscript('');
            setFullTranscript('');
            alert('Speech recognition error: ' + event.error);
        };
        recognition.onend = () => {
            setShowVoiceModal(false);
            setLiveTranscript('');
            setFullTranscript('');
        };
        recognition.start();
    };

    const settingsItems = [
        {
            label: 'Manage API Key',
            icon: 'pi pi-key',
            command: () => setShowApiKeyModal(true),
        },
        {
            label: 'Voice Command List',
            icon: 'pi pi-info-circle',
            command: () => setShowVoiceInfoModal(true),
        },
        {
            label: 'Manage API Docs',
            icon: 'pi pi-link',
            command: () => setShowManageDocs(true),
        },
        {
            label: 'Create Prompt',
            icon: 'pi pi-plus-circle',
            command: () => setShowPromptCreator(true),
        },
    ];

    const renderHeader = () => (
        <TableGroupHeader
            size={size}
            setSize={setSize}
            sizeOptions={sizeOptions}
            globalFilterValue={globalFilterValue}
            onGlobalFilterChange={onGlobalFilterChange}
            onRefresh={fetchEndpoints}
            clearFilter={clearFilter}
            extraButtonsTemplate={() => (
                <div className="flex gap-2">
                    <Button
                        icon="pi pi-plus"
                        onClick={() => setShowCreateModal(true)}
                        className="p-button-secondary"
                    />
                    <Button
                        icon="pi pi-comments"
                        onClick={() => setShowChatGptModal(true)}
                        className="p-button-primary"
                    />
                    <SettingsMenu
                        items={settingsItems}
                        buttonClassName="p-button-secondary"
                    />
                    <Button
                        icon="pi pi-volume-up"
                        onClick={handleVoiceInput}
                        className="p-button-secondary"
                        disabled={chatLoading}
                    />
                </div>
            )}
        />
    );

    const serverPath = import.meta.env.VITE_API_BASE_URL || window.location.origin;

    const generateCurlCommand = (endpoint: any, apiKey: string) => {
        const backendUrl = `${serverPath}/api/v1/canvas-api/proxy-external`;
        const method = endpoint.method || 'GET';
        const path = endpoint.path;
        const header = `-H 'Authorization: Bearer ${apiKey}'`;
        const data = `-d '{"path": "${path}", "method": "${method}"}'`;
        return `curl -X POST ${header} -H 'Content-Type: application/json' ${data} '${backendUrl}'`;
    };

    const renderCallButton = (rowData: any) => (
        <div className="flex gap-2">
            <Button icon="pi pi-play" onClick={() => handleCallEndpoint(rowData)} />
            <Button icon="pi pi-pencil" className="p-button-warning" onClick={() => openEditModal(rowData)} />
            <Button icon="pi pi-trash" className="p-button-danger" onClick={() => handleDeleteEndpoint(rowData)} />
            <span>
                <Button icon="pi pi-copy" className="p-button-secondary" onClick={() => {
                    if (!apiKey) {
                        if (toast.current) {
                            toast.current.show({ severity: 'warn', summary: 'API Key Required', detail: 'Please generate or enter your API key first.' });
                        }
                        return;
                    }
                    const curl = generateCurlCommand(rowData, apiKey);
                    navigator.clipboard.writeText(curl);
                    if (toast.current) {
                        toast.current.show({ severity: 'info', summary: 'Copied', detail: 'cURL command copied to clipboard!' });
                    }
                }} data-pr-tooltip="Copy cURL command" />
                <Tooltip target=".p-button-secondary" content="Copy cURL command" />
            </span>
        </div>
    );

    const renderCallModalHeader = () => (
        <TableGroupHeader
            size={callModalSize}
            setSize={setCallModalSize}
            sizeOptions={sizeOptions}
            globalFilterValue={callModalGlobalFilterValue}
            onGlobalFilterChange={onCallModalGlobalFilterChange}
            onRefresh={() => handleCallEndpoint(callEndpoint)}
            clearFilter={clearCallModalFilter}
        />
    );

    // Compute filtered endpoints for search-everything
    const filteredEndpoints = globalFilterValue
        ? endpoints.filter(endpoint =>
            Object.values(endpoint)
                .join(' ')
                .toLowerCase()
                .includes(globalFilterValue.toLowerCase())
        )
        : endpoints;

    return (
        <div className='w-full h-full p-6'>
            <h2 className='text-xl font-semibold mb-4'>Canvas Endpoints</h2>
            <Toast ref={toast} position="bottom-right" />
            <TableGroup
                body={filteredEndpoints}
                size={size}
                header={renderHeader}
                filters={filters}
                rows={10}
                paginator
                showGridlines={true}
                removableSort={true}
                dragSelection={false}
                emptyMessage='No endpoints found.'
            >
                <Column field='id' header='ID' sortable filter style={{ width: '10%' }} />
                <Column field='name' header='Name' sortable filter style={{ width: '20%' }} />
                <Column field='method' header='Method' sortable filter style={{ width: '10%' }} />
                <Column field='path' header='Path' sortable filter style={{ width: '25%' }} />
                <Column field='publicPath' header='Public Path' sortable filter style={{ width: '15%' }} />
                <Column header='Action' body={renderCallButton} style={{ width: '20%' }} />
            </TableGroup>

            <Dialog
                visible={showCreateModal}
                onHide={handleModalHide}
                header={editEndpointId ? 'Edit Endpoint' : 'Create Endpoint'}
                style={{ width: '40vw' }}
                modal
                footer={
                    <div>
                        <Button label="Cancel" onClick={handleModalHide} className="mr-2" />
                        <Button label={editEndpointId ? 'Save' : 'Create'} onClick={handleSaveEndpoint} className="bg-blue-500 text-white" />
                    </div>
                }
            >
                <div className="p-fluid">
                    <div className="p-field">
                        <label htmlFor="name">Name</label>
                        <InputText id="name" value={newEndpoint.name} onChange={(e) => setNewEndpoint({ ...newEndpoint, name: e.target.value })} />
                    </div>
                    <div className="p-field">
                        <label htmlFor="method">Method</label>
                        <InputText id="method" value={newEndpoint.method} onChange={(e) => setNewEndpoint({ ...newEndpoint, method: e.target.value })} />
                    </div>
                    <div className="p-field">
                        <label htmlFor="path">Path</label>
                        <InputText id="path" value={newEndpoint.path} onChange={(e) => setNewEndpoint({ ...newEndpoint, path: e.target.value })} />
                    </div>
                    <div className="p-field">
                        <label htmlFor="publicPath">Public Path</label>
                        <InputText id="publicPath" value={newEndpoint.publicPath || ''} onChange={(e) => setNewEndpoint({ ...newEndpoint, publicPath: e.target.value })} />
                    </div>
                    <div className="p-field">
                        <label htmlFor="description">Description</label>
                        <InputText id="description" value={newEndpoint.description} onChange={(e) => setNewEndpoint({ ...newEndpoint, description: e.target.value })} />
                    </div>
                </div>
            </Dialog>

            <Dialog
                visible={showCallModal}
                onHide={() => setShowCallModal(false)}
                header={callEndpoint ? `Response for ${callEndpoint.name}` : 'Endpoint Response'}
                style={{ width: '96vw', maxWidth: 1800, minHeight: 600 }}
                modal
            >
                {callModalLoading ? (
                    <div className="flex justify-center items-center" style={{ minHeight: 200 }}>
                        <ProgressSpinner />
                    </div>
                ) : callModalData.length > 0 ? (
                    <div style={{ display: 'flex', flexDirection: 'column', height: '70vh' }}>
                        <div style={{ flex: 1, overflow: 'auto', maxWidth: '100%' }}>
                            <TableGroup
                                body={callModalData.slice(callModalFirst, callModalFirst + callModalRows)}
                                size={callModalSize}
                                header={renderCallModalHeader}
                                filters={callModalFilters}
                                rows={callModalRows}
                                paginator={false}
                                showGridlines={true}
                                removableSort={true}
                                dragSelection={false}
                                emptyMessage='No data found.'
                            >
                                {callModalColumns.map((col) => (
                                    <Column
                                        key={col}
                                        field={col}
                                        header={col}
                                        sortable
                                        filter
                                        body={rowData => {
                                            const value = rowData[col];
                                            if (typeof value === 'object' && value !== null) {
                                                return <span style={{ fontFamily: 'monospace', fontSize: 12 }}>{JSON.stringify(value)}</span>;
                                            }
                                            return value;
                                        }}
                                    />
                                ))}
                            </TableGroup>
                        </div>
                        <div style={{ position: 'sticky', bottom: 0, background: '#fff', padding: '8px 0 0 0', zIndex: 2, borderTop: '1px solid #eee' }}>
                            <div style={{ width: '100%' }}>
                                <span style={{ float: 'right' }}>
                                    <span style={{ marginRight: 16 }}>
                                        Showing {callModalFirst + 1} to {Math.min(callModalFirst + callModalRows, callModalData.length)} of {callModalData.length} entries
                                    </span>
                                    <select
                                        value={callModalRows}
                                        onChange={e => setCallModalRows(Number(e.target.value))}
                                        style={{ marginRight: 8 }}
                                    >
                                        {[10, 25, 50].map(opt => (
                                            <option key={opt} value={opt}>{opt}</option>
                                        ))}
                                    </select>
                                    <button onClick={() => setCallModalFirst(0)} disabled={callModalFirst === 0}>{'<<'}</button>
                                    <button onClick={() => setCallModalFirst(Math.max(0, callModalFirst - callModalRows))} disabled={callModalFirst === 0}>{'<'}</button>
                                    <span style={{ margin: '0 8px' }}>{Math.floor(callModalFirst / callModalRows) + 1}</span>
                                    <button onClick={() => setCallModalFirst(Math.min(callModalFirst + callModalRows, callModalData.length - callModalRows))} disabled={callModalFirst + callModalRows >= callModalData.length}>{'>'}</button>
                                    <button onClick={() => setCallModalFirst(Math.max(0, callModalData.length - callModalRows))} disabled={callModalFirst + callModalRows >= callModalData.length}>{'>>'}</button>
                                </span>
                            </div>
                        </div>
                    </div>
                ) : (
                    <div>No data found or error.</div>
                )}
            </Dialog>

            <Dialog
                visible={showChatGptModal}
                onHide={() => setShowChatGptModal(false)}
                header="ChatGPT Assistant"
                style={{ width: '50vw' }}
            >
                <div className="flex flex-col h-[60vh]">
                    <div className="flex-1 overflow-auto mb-4 p-2 bg-gray-50 rounded">
                        {chatMessages.length === 0 && <div className="text-gray-400 text-center mt-8">Start a conversation with ChatGPT...</div>}
                        {chatMessages.map((msg, idx) => (
                            <div key={idx} className={`my-2 p-2 rounded-lg max-w-[80%] ${msg.role === 'user' ? 'bg-blue-100 ml-auto' : 'bg-gray-200'}`}>
                                <span>{msg.content}</span>
                            </div>
                        ))}
                    </div>
                    <div className="flex gap-2 mt-auto">
                        <input
                            type="text"
                            className="flex-1 p-2 border rounded"
                            placeholder="Type your message..."
                            value={chatPrompt}
                            onChange={e => setChatPrompt(e.target.value)}
                            onKeyDown={handleChatPromptKeyDown}
                            disabled={chatLoading}
                        />
                        <Button
                            icon="pi pi-send"
                            onClick={() => handleChatPromptSend()}
                            loading={chatLoading}
                            disabled={!chatPrompt.trim() || chatLoading}
                        />
                    </div>
                    {chatError && <div className="text-red-500 mt-2">{chatError}</div>}
                </div>
            </Dialog>

            <Dialog
                visible={showApiKeyModal}
                onHide={() => setShowApiKeyModal(false)}
                header="API Key Management"
                footer={
                    <div>
                        <Button
                            label={apiKey ? "Regenerate API Key" : "Create API Key"}
                            icon="pi pi-refresh"
                            onClick={handleApiKey}
                            loading={apiKeyLoading}
                            className="p-button-danger"
                        />
                        <Button
                            label="Close"
                            icon="pi pi-times"
                            onClick={() => setShowApiKeyModal(false)}
                            className="p-button-text"
                        />
                    </div>
                }
            >
                {apiKey && (
                    <div>
                        <div className="mb-2 font-bold">Current API Key:</div>
                        <div className="font-mono bg-gray-100 p-2 rounded">{apiKey}</div>
                        <div className="mt-2 text-sm text-red-500">
                            Regenerating will invalidate the old key.
                        </div>
                    </div>
                )}
                {!apiKey && (
                    <div>
                        <div>No API key exists for this app. Click "Create API Key" to generate one.</div>
                    </div>
                )}
                {apiKeyError && <div className="text-red-500 mt-2">{apiKeyError}</div>}
            </Dialog>

            <Dialog
                visible={showVoiceModal}
                onHide={() => setShowVoiceModal(false)}
                header="Listening..."
                modal
                style={{ minWidth: 400, textAlign: 'center' }}
                closable={false}
            >
                <div style={{ fontSize: 24, minHeight: 60 }}>{fullTranscript || <span className="text-gray-400">Say something…</span>}</div>
                <div className="mt-4 text-sm text-gray-500">Speak your command or query.</div>
            </Dialog>

            <Dialog
                visible={showVoiceInfoModal}
                onHide={() => setShowVoiceInfoModal(false)}
                header="Voice Command List"
                modal
                style={{ minWidth: 400 }}
            >
                <ul style={{ textAlign: 'left', lineHeight: 2 }}>
                    <li><b>show/view/open endpoint [name or id]</b> — Open an endpoint's table view</li>
                    <li><b>delete endpoint [name or id]</b> — Delete an endpoint</li>
                    <li><b>search [term]</b> — Filter endpoints</li>
                    <li><b>modify endpoint [id]</b> — Start editing an endpoint by voice</li>
                    <li><b>method is [GET/POST/PUT/DELETE/PATCH]</b> — Set method (in edit mode)</li>
                    <li><b>path is [your path]</b> — Set path (in edit mode, say 'slash' for /, 'colon id' for :id)</li>
                    <li><b>open command list</b> — Show this help</li>
                </ul>
            </Dialog>

            <ManageDocsModal visible={showManageDocs} onHide={() => setShowManageDocs(false)} />

            <PromptCreatorModal
                visible={showPromptCreator}
                onHide={() => setShowPromptCreator(false)}
                appId={appId || 0}
            />
        </div>
    );
} 