import { ArrowsAltOutlined, DeleteOutlined, PlayCircleOutlined, PlusOutlined, SaveOutlined, ShrinkOutlined } from '@ant-design/icons';
import { Button, Empty, Input, Modal, Space, Tabs, Tooltip, Typography, message } from 'antd';
import { useEffect, useMemo, useState, type ReactNode } from 'react';
import { useNavigate } from 'react-router-dom';
import { canUseChatType, resolveCurrentChatPermissionRoles, useChatTypeRegistry } from '../../../app/main/chatTypeAccess';
import { renderModalWithEnterConfirm } from '../../../app/main/modalKeyboard';
import { createChatConversationRoom, fetchChatConversations } from '../../../app/main/mainChatPanel';
import { buildChatPath } from '../../../app/main/routes';
import { useTokenAccess } from '../../../app/main/tokenAccess';
import { SelectUI, type SelectOptionItem } from '../../../components/inputs/select';
import { listSavedLayouts, saveLayout, type SavedLayoutRecord } from '../../../views/play/layoutStorage';
import { isReusableLayoutConversation, resolveLayoutCodexRequestSocketUrl, resolveLayoutConversationTitle } from './featureMenu.chat';
import type { FeatureMenuTabKey, LayoutInteractionRule } from './featureMenu.types';
import { buildFeatureMenuPrompt, normalizeLayoutInteractions } from './featureMenu.utils';
import './FeatureMenuLayoutPage.css';

const { Paragraph, Text } = Typography;

type FeatureMenuLayoutPageProps = {
  layoutId: string;
  savedLayouts: SavedLayoutRecord[];
  onSavedLayoutsChange?: (layouts: SavedLayoutRecord[]) => void;
};

export function FeatureMenuLayoutPage({ layoutId, savedLayouts, onSavedLayoutsChange }: FeatureMenuLayoutPageProps) {
  const navigate = useNavigate();
  const { chatTypes } = useChatTypeRegistry();
  const { hasAccess } = useTokenAccess();
  const [messageApi, contextHolder] = message.useMessage();
  const [modalApi, modalContextHolder] = Modal.useModal();
  const [selectedLayoutId, setSelectedLayoutId] = useState<string | null>(null);
  const [selectedFeatureId, setSelectedFeatureId] = useState<string | null>(null);
  const [activeTab, setActiveTab] = useState<FeatureMenuTabKey>('description');
  const [isEditorMaximized, setIsEditorMaximized] = useState(false);
  const [draftBody, setDraftBody] = useState('');
  const [isSaving, setIsSaving] = useState(false);
  const [isSending, setIsSending] = useState(false);
  const [selectedChatTypeId, setSelectedChatTypeId] = useState<string | null>(null);

  const chatPermissionRoles = useMemo(() => resolveCurrentChatPermissionRoles(hasAccess), [hasAccess]);
  const availableChatTypes = useMemo(
    () => chatTypes.filter((item) => canUseChatType(item, chatPermissionRoles)),
    [chatPermissionRoles, chatTypes],
  );
  const selectedChatType = useMemo(
    () => availableChatTypes.find((item) => item.id === selectedChatTypeId) ?? availableChatTypes[0] ?? null,
    [availableChatTypes, selectedChatTypeId],
  );

  const layoutOptions = useMemo<SelectOptionItem[]>(
    () =>
      [...savedLayouts]
        .sort((left, right) => {
          if (left.id === layoutId) {
            return -1;
          }

          if (right.id === layoutId) {
            return 1;
          }

          return 0;
        })
        .map((item) => ({
          code: item.id,
          value: item.name,
        })),
    [layoutId, savedLayouts],
  );

  useEffect(() => {
    if (savedLayouts.length === 0) {
      setSelectedLayoutId(null);
      return;
    }

    if (selectedLayoutId && savedLayouts.some((item) => item.id === selectedLayoutId)) {
      return;
    }

    const fallback = savedLayouts.find((item) => item.id === layoutId) ?? savedLayouts[0];
    setSelectedLayoutId(fallback?.id ?? null);
  }, [layoutId, savedLayouts, selectedLayoutId]);

  useEffect(() => {
    if (availableChatTypes.length === 0) {
      setSelectedChatTypeId(null);
      return;
    }

    if (selectedChatTypeId && availableChatTypes.some((item) => item.id === selectedChatTypeId)) {
      return;
    }

    setSelectedChatTypeId(availableChatTypes[0]?.id ?? null);
  }, [availableChatTypes, selectedChatTypeId]);

  const selectedLayout = selectedLayoutId ? savedLayouts.find((item) => item.id === selectedLayoutId) ?? null : null;
  const selectedLayoutInteractions = useMemo<LayoutInteractionRule[]>(
    () => normalizeLayoutInteractions(selectedLayout?.tree ?? null),
    [selectedLayout?.tree],
  );

  const featureOptions = useMemo<SelectOptionItem[]>(
    () =>
      selectedLayoutInteractions.map((item, index) => ({
        code: item.id,
        value: item.title || `기능설명 ${index + 1}`,
      })),
    [selectedLayoutInteractions],
  );

  useEffect(() => {
    if (featureOptions.length === 0) {
      setSelectedFeatureId(null);
      return;
    }

    if (selectedFeatureId && featureOptions.some((item) => item.code === selectedFeatureId)) {
      return;
    }

    setSelectedFeatureId(featureOptions[0]?.code ?? null);
  }, [featureOptions, selectedFeatureId]);

  const selectedFeature =
    selectedFeatureId ? selectedLayoutInteractions.find((item) => item.id === selectedFeatureId) ?? null : null;

  useEffect(() => {
    setDraftBody(selectedFeature?.description ?? '');
    setActiveTab('description');
  }, [selectedFeature?.description, selectedFeature?.id]);

  const isDirty = Boolean(selectedFeature && selectedFeature.description !== draftBody);

  const refreshLayouts = async () => {
    const nextLayouts = await listSavedLayouts();
    onSavedLayoutsChange?.(nextLayouts);
    return nextLayouts;
  };

  const handleSave = async () => {
    if (!selectedLayout || !selectedFeature) {
      return;
    }

    const nextDescription = draftBody.trim();

    if (!nextDescription) {
      void messageApi.warning('기능설명 본문을 입력하세요.');
      return;
    }

    setIsSaving(true);

    try {
      const nextInteractions = selectedLayoutInteractions.map((item) =>
        item.id === selectedFeature.id
          ? {
              ...item,
              description: nextDescription,
            }
          : item,
      );
      const nextTree =
        selectedLayout.tree && typeof selectedLayout.tree === 'object'
          ? {
              ...(selectedLayout.tree as Record<string, unknown>),
              interactions: nextInteractions,
              interactionMode: 'scoped-v2',
            }
          : {
              root: null,
              interactions: nextInteractions,
              interactionMode: 'scoped-v2',
            };

      await saveLayout({
        ...selectedLayout,
        updatedAt: new Date().toISOString(),
        tree: nextTree,
      });

      await refreshLayouts();
      void messageApi.success('기능설명을 저장했습니다.');
    } catch (error) {
      void messageApi.error(error instanceof Error ? error.message : '기능설명 저장에 실패했습니다.');
    } finally {
      setIsSaving(false);
    }
  };

  const handleAdd = async () => {
    if (!selectedLayout) {
      return;
    }

    const nextIndex = selectedLayoutInteractions.length + 1;
    const nextFeatureId =
      typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
        ? crypto.randomUUID()
        : `feature-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
    const nextFeature: LayoutInteractionRule = {
      id: nextFeatureId,
      title: `기능설명 ${nextIndex}`,
      description: '',
      implementationNotes: '',
    };
    const nextInteractions = [...selectedLayoutInteractions, nextFeature];
    const nextTree =
      selectedLayout.tree && typeof selectedLayout.tree === 'object'
        ? {
            ...(selectedLayout.tree as Record<string, unknown>),
            interactions: nextInteractions,
            interactionMode: 'scoped-v2',
          }
        : {
            root: null,
            interactions: nextInteractions,
            interactionMode: 'scoped-v2',
          };

    setIsSaving(true);

    try {
      await saveLayout({
        ...selectedLayout,
        updatedAt: new Date().toISOString(),
        tree: nextTree,
      });

      await refreshLayouts();
      setSelectedFeatureId(nextFeatureId);
      setDraftBody('');
      setActiveTab('description');
      void messageApi.success('기능설명 항목을 추가했습니다.');
    } catch (error) {
      void messageApi.error(error instanceof Error ? error.message : '기능설명 항목 추가에 실패했습니다.');
    } finally {
      setIsSaving(false);
    }
  };

  const handleDelete = () => {
    if (!selectedLayout || !selectedFeature) {
      return;
    }

    void modalApi.confirm({
      title: '선택한 기능설명을 삭제할까요?',
      content: '삭제 후 되돌릴 수 없습니다.',
      okText: '삭제',
      cancelText: '취소',
      okButtonProps: { danger: true },
      modalRender: renderModalWithEnterConfirm,
      async onOk() {
        const nextInteractions = selectedLayoutInteractions.filter((item) => item.id !== selectedFeature.id);
        const nextTree =
          selectedLayout.tree && typeof selectedLayout.tree === 'object'
            ? {
                ...(selectedLayout.tree as Record<string, unknown>),
                interactions: nextInteractions,
                interactionMode: 'scoped-v2',
              }
            : {
                root: null,
                interactions: nextInteractions,
                interactionMode: 'scoped-v2',
              };

        await saveLayout({
          ...selectedLayout,
          updatedAt: new Date().toISOString(),
          tree: nextTree,
        });

        await refreshLayouts();
        void messageApi.success('기능설명을 삭제했습니다.');
      },
    });
  };

  const handleCodexExecute = async () => {
    if (!selectedLayout || !selectedChatType) {
      if (!selectedChatType) {
        void messageApi.error('사용 가능한 채팅유형이 없어 Codex 실행을 보낼 수 없습니다.');
      }
      return;
    }

    const rules = selectedFeature ? [selectedFeature] : selectedLayoutInteractions;
    const prompt = buildFeatureMenuPrompt(selectedLayout, rules);
    const conversationTitle = resolveLayoutConversationTitle(selectedLayout.name);

    setIsSending(true);

    try {
      const conversations = await fetchChatConversations();
      const matchedConversation = conversations.find((item) =>
        isReusableLayoutConversation(item, conversationTitle, selectedChatType.id),
      );
      const shouldCreateConversation = !matchedConversation;
      const targetSessionId =
        matchedConversation?.sessionId ||
        (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
          ? crypto.randomUUID()
          : `chat-session-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`);

      if (shouldCreateConversation) {
        await createChatConversationRoom({
          sessionId: targetSessionId,
          title: conversationTitle,
          chatTypeId: selectedChatType.id,
          lastChatTypeId: selectedChatType.id,
          contextLabel: selectedChatType.name,
          contextDescription: selectedChatType.description,
          notifyOffline: true,
        });
      }

      const requestId = `client-${Date.now().toString(36)}`;
      const socketUrl = resolveLayoutCodexRequestSocketUrl(targetSessionId);

      await new Promise<void>((resolve, reject) => {
        const socket = new WebSocket(socketUrl);
        const timeoutId = window.setTimeout(() => {
          socket.close();
          reject(new Error('Codex 요청 연결 시간이 초과되었습니다.'));
        }, 8000);

        socket.addEventListener('open', () => {
          socket.send(
            JSON.stringify({
              type: 'message:send',
              payload: {
                text: prompt,
                chatTypeId: selectedChatType.id,
                chatTypeLabel: selectedChatType.name,
                chatTypeDescription: selectedChatType.description,
                requestId,
                mode: 'queue',
              },
            }),
          );

          window.setTimeout(() => {
            window.clearTimeout(timeoutId);
            socket.close();
            resolve();
          }, 120);
        });

        socket.addEventListener('error', () => {
          window.clearTimeout(timeoutId);
          reject(new Error('Codex 요청 연결에 실패했습니다.'));
        });

        socket.addEventListener('close', (event) => {
          if (!event.wasClean && event.code !== 1000) {
            window.clearTimeout(timeoutId);
            reject(new Error('Codex 요청 연결이 비정상 종료되었습니다.'));
          }
        });
      });

      void messageApi.success('Codex 실행 요청을 전송했습니다.');
      navigate(`${buildChatPath('live')}?sessionId=${encodeURIComponent(targetSessionId)}`);
    } catch (error) {
      void messageApi.error(error instanceof Error ? error.message : 'Codex 실행 요청에 실패했습니다.');
    } finally {
      setIsSending(false);
    }
  };

  const editorTabItems = [
    {
      key: 'description',
      label: '기능설명 입력',
      children: (
        <div className="feature-menu-layout-page__editor-fields">
          <Input.TextArea
            value={draftBody}
            placeholder="기능설명 본문을 입력하세요."
            autoSize={false}
            className="feature-menu-layout-page__textarea"
            onChange={(event) => {
              setDraftBody(event.target.value);
            }}
          />
        </div>
      ),
    },
    {
      key: 'notes',
      label: 'Codex 설명',
      children: selectedFeature?.implementationNotes.trim() ? (
        <div className="feature-menu-layout-page__notes">
          <Paragraph className="feature-menu-layout-page__notes-body">{selectedFeature.implementationNotes}</Paragraph>
        </div>
      ) : (
        <div className="feature-menu-layout-page__notes feature-menu-layout-page__notes--empty">
          <Empty
            image={Empty.PRESENTED_IMAGE_SIMPLE}
            description={null}
            className="feature-menu-layout-page__notes-empty"
          />
        </div>
      ),
    },
  ] satisfies Array<{ key: FeatureMenuTabKey; label: string; children: ReactNode }>;

  return (
    <div
      className={`feature-menu-layout-page${isEditorMaximized ? ' feature-menu-layout-page--editor-maximized' : ''}`}
      data-layout-id={layoutId}
    >
      {contextHolder}
      {modalContextHolder}
      <section className="feature-menu-layout-page__filters">
        <div className="feature-menu-layout-page__field">
          <Text className="feature-menu-layout-page__field-label">레이아웃명</Text>
          <SelectUI
            data={layoutOptions}
            value={selectedLayoutId ?? undefined}
            allowClear={false}
            className="feature-menu-layout-page__select"
            onChange={(nextCode) => {
              setSelectedLayoutId(nextCode ?? null);
            }}
          />
        </div>
        <div className="feature-menu-layout-page__field">
          <Text className="feature-menu-layout-page__field-label">기능설명 선택</Text>
          <SelectUI
            data={featureOptions}
            value={selectedFeatureId ?? undefined}
            allowClear={false}
            disabled={featureOptions.length === 0}
            className="feature-menu-layout-page__select"
            onChange={(nextCode) => {
              setSelectedFeatureId(nextCode ?? null);
            }}
          />
        </div>
        <div className="feature-menu-layout-page__field">
          <Text className="feature-menu-layout-page__field-label">Codex 채팅유형</Text>
          <SelectUI
            data={availableChatTypes.map((item) => ({
              code: item.id,
              value: item.name,
            }))}
            value={selectedChatType?.id ?? undefined}
            allowClear={false}
            disabled={availableChatTypes.length === 0}
            className="feature-menu-layout-page__select"
            onChange={(nextCode) => {
              setSelectedChatTypeId(nextCode ?? null);
            }}
          />
        </div>
        <Tooltip title="Codex 실행">
          <Button
            type="primary"
            size="large"
            icon={<PlayCircleOutlined />}
            className="feature-menu-layout-page__run-button"
            disabled={!selectedLayout || !selectedChatType}
            loading={isSending}
            aria-label="Codex 실행"
            onClick={() => {
              void handleCodexExecute();
            }}
          />
        </Tooltip>
      </section>

      <section className="feature-menu-layout-page__editor-shell">
        {selectedFeature ? (
          <>
            <div className="feature-menu-layout-page__editor-toolbar">
              <Space size={8} wrap className="feature-menu-layout-page__tab-actions">
                <Tooltip title="추가">
                  <Button
                    icon={<PlusOutlined />}
                    disabled={!selectedLayout}
                    loading={isSaving && !selectedFeature}
                    aria-label="추가"
                    onClick={() => {
                      void handleAdd();
                    }}
                  />
                </Tooltip>
                <Tooltip title={isEditorMaximized ? '입력 축소' : '입력 최대화'}>
                  <Button
                    type={isEditorMaximized ? 'primary' : 'default'}
                    icon={isEditorMaximized ? <ShrinkOutlined /> : <ArrowsAltOutlined />}
                    aria-label={isEditorMaximized ? '입력 축소' : '입력 최대화'}
                    onClick={() => {
                      setIsEditorMaximized((current) => !current);
                    }}
                  />
                </Tooltip>
                <Tooltip title="저장">
                  <Button
                    type="primary"
                    icon={<SaveOutlined />}
                    disabled={!selectedFeature || !isDirty}
                    loading={isSaving}
                    aria-label="저장"
                    onClick={() => {
                      void handleSave();
                    }}
                  />
                </Tooltip>
                <Tooltip title="삭제">
                  <Button
                    danger
                    icon={<DeleteOutlined />}
                    disabled={!selectedFeature}
                    aria-label="삭제"
                    onClick={handleDelete}
                  />
                </Tooltip>
              </Space>
            </div>
            <Tabs
              activeKey={activeTab}
              className="feature-menu-layout-page__tabs"
              tabPosition="top"
              items={editorTabItems}
              onChange={(nextKey) => {
                setActiveTab(nextKey as FeatureMenuTabKey);
              }}
            />
          </>
        ) : (
          <div className="feature-menu-layout-page__empty">
            <Empty description="선택 가능한 기능설명 항목이 없습니다." />
          </div>
        )}
      </section>
    </div>
  );
}
