import { useState, useCallback, useRef } from 'react';
import { TextInput, Alert } from 'react-native';
import * as ImagePicker from 'expo-image-picker';

import { uploadService } from '@/services/upload';
import type { EditorBlock, TextBlock, ImageBlock } from './blockEditorTypes';
import {
  generateBlockId,
  createInitialTextBlock,
  remapMentionsBefore,
  remapMentionsAfter,
  mergeMentions,
} from './blockEditorTypes';
import { blocksToSegments, blocksToContent } from './blocksToSegments';

interface MentionData {
  endIndex: number;
  userId: string;
  nickname: string;
}

export function useBlockEditor(initialBlocks?: EditorBlock[]) {
  const [blocks, setBlocks] = useState<EditorBlock[]>(initialBlocks && initialBlocks.length > 0 ? initialBlocks : [createInitialTextBlock()]);
  const [activeBlockId, setActiveBlockId] = useState<string | null>(null);
  const inputRefs = useRef<Map<string, TextInput>>(new Map());
  const blockCursors = useRef<Map<string, { start: number; end: number }>>(new Map());

  const updateTextBlock = useCallback(
    (blockId: string, text: string, mentions: Map<number, MentionData>) => {
      setBlocks(prev =>
        prev.map(b =>
          b.id === blockId && b.type === 'text'
            ? { ...b, text, activeMentions: mentions }
            : b
        )
      );
    },
    [],
  );

  const focusBlock = useCallback((blockId: string) => {
    requestAnimationFrame(() => {
      const ref = inputRefs.current.get(blockId);
      ref?.focus();
    });
  }, []);

  const removeImageBlock = useCallback((blockId: string) => {
    setBlocks(prev => {
      const index = prev.findIndex(b => b.id === blockId);
      if (index < 0) return prev;

      const updated = prev.filter(b => b.id !== blockId);

      // Merge adjacent text blocks if both neighbors are text
      const before = updated[index - 1];
      const after = updated[index]; // was index + 1 before removal

      if (
        before && after &&
        before.type === 'text' && after.type === 'text'
      ) {
        const mergedMentions = mergeMentions(
          before.activeMentions,
          before.text.length,
          after.activeMentions,
        );
        const mergedBlock: TextBlock = {
          ...before,
          text: before.text + after.text,
          activeMentions: mergedMentions,
        };
        updated[index - 1] = mergedBlock;
        updated.splice(index, 1);
        setActiveBlockId(mergedBlock.id);
        requestAnimationFrame(() => focusBlock(mergedBlock.id));
      }

      // Ensure at least one text block exists
      if (updated.length === 0) {
        const newBlock = createInitialTextBlock();
        updated.push(newBlock);
        setActiveBlockId(newBlock.id);
      }

      return updated;
    });
  }, [focusBlock]);

  const uploadImageByLocalUri = useCallback(
    async (asset: ImagePicker.ImagePickerAsset) => {
      try {
        const uploadResult = await uploadService.uploadImage({
          uri: asset.uri,
          type: asset.mimeType || 'image/jpeg',
        });

        if (uploadResult) {
          setBlocks(prev =>
            prev.map(b =>
              b.type === 'image' && b.localUri === asset.uri && !b.remoteUrl
                ? {
                    ...b,
                    remoteUrl: uploadResult.url,
                    uploading: false,
                    width: uploadResult.width ?? b.width,
                    height: uploadResult.height ?? b.height,
                  }
                : b
            )
          );
        } else {
          // Upload failed
          setBlocks(prev =>
            prev.map(b =>
              b.type === 'image' && b.localUri === asset.uri && !b.remoteUrl
                ? { ...b, uploading: false }
                : b
            )
          );
          Alert.alert('上传失败', '图片上传失败，请重试');
        }
      } catch {
        setBlocks(prev =>
          prev.map(b =>
            b.type === 'image' && b.localUri === asset.uri && !b.remoteUrl
              ? { ...b, uploading: false }
              : b
          )
        );
      }
    },
    [],
  );

  const insertImagesFromAssets = useCallback(
    (assets: ImagePicker.ImagePickerAsset[]) => {
      if (!assets.length) return;

      setBlocks(prev => {
        let updated = [...prev];
        let currentActiveId = activeBlockId;
        let insertAfterIndex = updated.findIndex(b => b.id === currentActiveId);

        if (insertAfterIndex < 0) {
          insertAfterIndex = updated.length - 1;
        }

        for (const asset of assets) {
          const activeBlock = updated[insertAfterIndex];
          const cursorPos = activeBlock && activeBlock.type === 'text'
            ? (blockCursors.current.get(activeBlock.id)?.start ?? activeBlock.text.length)
            : 0;

          // Create image block
          const imageBlockId = generateBlockId();
          const imageBlock: ImageBlock = {
            id: imageBlockId,
            type: 'image',
            localUri: asset.uri,
            uploading: true,
            width: asset.width,
            height: asset.height,
          };

          if (activeBlock && activeBlock.type === 'text') {
            const textBefore = activeBlock.text.slice(0, cursorPos);
            const textAfter = activeBlock.text.slice(cursorPos);
            const mentionsBefore = remapMentionsBefore(activeBlock.activeMentions, cursorPos);
            const mentionsAfter = remapMentionsAfter(activeBlock.activeMentions, cursorPos, activeBlock.text.length);

            // Create new text block for text after cursor
            const newTextBlockId = generateBlockId();
            const newTextBlock: TextBlock = {
              id: newTextBlockId,
              type: 'text',
              text: textAfter,
              activeMentions: mentionsAfter,
            };

            // Update current block to text before cursor
            updated[insertAfterIndex] = {
              ...activeBlock,
              text: textBefore,
              activeMentions: mentionsBefore,
            };

            // Insert image block and new text block
            updated.splice(insertAfterIndex + 1, 0, imageBlock, newTextBlock);

            // Update index for next image insertion
            insertAfterIndex = insertAfterIndex + 2;

            // Focus the new text block after the image
            setActiveBlockId(newTextBlockId);
            requestAnimationFrame(() => focusBlock(newTextBlockId));
          } else {
            // Insert after the current block (which is an image)
            updated.splice(insertAfterIndex + 1, 0, imageBlock);
            insertAfterIndex = insertAfterIndex + 1;
          }
        }

        return updated;
      });

      // Start async uploads
      for (const asset of assets) {
        uploadImageByLocalUri(asset);
      }
    },
    [activeBlockId, focusBlock, uploadImageByLocalUri],
  );

  const insertImage = useCallback(async () => {
    const permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync();
    if (!permissionResult.granted) {
      Alert.alert('权限不足', '需要访问相册权限来选择图片');
      return;
    }

    const result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: 'images',
      allowsMultipleSelection: true,
      selectionLimit: 0,
      quality: 1,
    });

    if (!result.canceled && result.assets) {
      insertImagesFromAssets(result.assets);
    }
  }, [insertImagesFromAssets]);

  const insertCameraPhoto = useCallback(async () => {
    const permissionResult = await ImagePicker.requestCameraPermissionsAsync();
    if (!permissionResult.granted) {
      Alert.alert('权限不足', '需要访问相机权限来拍照');
      return;
    }

    const result = await ImagePicker.launchCameraAsync({
      allowsEditing: true,
      quality: 0.8,
    });

    if (!result.canceled && result.assets && result.assets[0]) {
      insertImagesFromAssets([result.assets[0]]);
    }
  }, [insertImagesFromAssets]);

  const insertTextAtCursor = useCallback((text: string) => {
    const activeId = activeBlockId;
    if (!activeId) return;

    setBlocks(prev =>
      prev.map(b => {
        if (b.id !== activeId || b.type !== 'text') return b;

        const cursorPos = blockCursors.current.get(b.id)?.start ?? b.text.length;
        const newText = b.text.slice(0, cursorPos) + text + b.text.slice(cursorPos);

        // Shift mentions after cursor
        const newMentions = new Map<number, MentionData>();
        b.activeMentions.forEach((mention, start) => {
          if (start >= cursorPos) {
            newMentions.set(start + text.length, {
              ...mention,
              endIndex: mention.endIndex + text.length,
            });
          } else {
            newMentions.set(start, mention);
          }
        });

        const newPosition = cursorPos + text.length;
        blockCursors.current.set(b.id, { start: newPosition, end: newPosition });

        return { ...b, text: newText, activeMentions: newMentions };
      })
    );
  }, [activeBlockId]);

  const getSegments = useCallback(() => blocksToSegments(blocks), [blocks]);
  const getContent = useCallback(() => blocksToContent(blocks), [blocks]);
  const hasUploadingImages = useCallback(() => blocks.some(b => b.type === 'image' && b.uploading), [blocks]);

  const registerInputRef = useCallback((blockId: string, ref: TextInput | null) => {
    if (ref) {
      inputRefs.current.set(blockId, ref);
    } else {
      inputRefs.current.delete(blockId);
    }
  }, []);

  const updateCursor = useCallback((blockId: string, selection: { start: number; end: number }) => {
    blockCursors.current.set(blockId, selection);
  }, []);

  return {
    blocks,
    activeBlockId,
    setActiveBlockId,
    updateTextBlock,
    removeImageBlock,
    insertImage,
    insertCameraPhoto,
    insertTextAtCursor,
    focusBlock,
    getSegments,
    getContent,
    hasUploadingImages,
    registerInputRef,
    updateCursor,
  };
}