import { Box, Flex, Text, Icon, useBreakpointValue } from '@chakra-ui/react';
import { useState, useCallback, useRef, useEffect } from 'react';
import { Settings, ChevronUp } from 'lucide-react';
import Controls from './Controls';
import Canvas from './Canvas';
import { createRenderer } from './renderer';
import { createInitialState, OVERLAY_TEXTURES } from './types';
import {
  loadImageFromFile,
  loadImageFromURL,
  loadVideoFromFile,
  loadVideoFromURL,
  copyCanvasToClipboard,
  copyDataURLToClipboard,
  exportCanvas,
  isClipboardImageSupported,
  generateProceduralTexture
} from './utils';
import { toaster } from '../../components/setup/toaster';

export default function TextureLab({ toolSelector }) {
  const isMobile = useBreakpointValue({ base: true, lg: false });
  const [mobileControlsOpen, setMobileControlsOpen] = useState(false);

  const canvasRef = useRef(null);
  const rendererRef = useRef(null);
  const renderTimeoutRef = useRef(null);
  const videoRef = useRef(null);
  const videoAnimationRef = useRef(null);

  const [state, setState] = useState(createInitialState);
  const {
    image,
    video,
    mediaType,
    corsError,
    effects,
    seed,
    previewQuality,
    exportFormat,
    exportQuality,
    exportScale,
    viewMode,
    isPlaying,
    currentTime,
    duration,
    isExporting,
    exportProgress,
    exportStatus
  } = state;

  useEffect(() => {
    if (canvasRef.current && !rendererRef.current) {
      try {
        rendererRef.current = createRenderer(canvasRef.current);
      } catch (err) {
        console.error('Failed to create renderer:', err);
        toaster.create({
          title: 'Renderer Error',
          description: 'Failed to initialize graphics renderer.',
          type: 'error',
          duration: 5000
        });
      }
    }

    return () => {
      if (rendererRef.current) {
        rendererRef.current.destroy();
        rendererRef.current = null;
      }
      if (videoRef.current) {
        videoRef.current.pause();
        if (videoRef.current.src && videoRef.current.src.startsWith('blob:')) {
          URL.revokeObjectURL(videoRef.current.src);
        }
        videoRef.current.src = '';
        videoRef.current = null;
      }
    };
  }, []);

  const renderPreview = useCallback(() => {
    if (!rendererRef.current) return;

    if (mediaType === 'video' && video) {
      rendererRef.current.updateVideoFrame(video);
      rendererRef.current.render(effects, seed, video.videoWidth, video.videoHeight);
      return;
    }

    if (!image) return;

    let targetWidth = image.width;
    let targetHeight = image.height;

    if (previewQuality === 'draft' && (image.width > 1000 || image.height > 1000)) {
      const scale = Math.min(1000 / image.width, 1000 / image.height);
      targetWidth = Math.round(image.width * scale);
      targetHeight = Math.round(image.height * scale);
    }

    rendererRef.current.render(effects, seed, targetWidth, targetHeight);
  }, [image, video, mediaType, effects, seed, previewQuality]);

  const rafRef = useRef(null);

  useEffect(() => {
    if (mediaType !== 'video' || !video || !isPlaying) {
      if (videoAnimationRef.current) {
        cancelAnimationFrame(videoAnimationRef.current);
        videoAnimationRef.current = null;
      }
      return;
    }

    const updateFrame = () => {
      if (!video.paused && !video.ended) {
        renderPreview();
        setState(s => ({ ...s, currentTime: video.currentTime }));
        videoAnimationRef.current = requestAnimationFrame(updateFrame);
      }
    };

    videoAnimationRef.current = requestAnimationFrame(updateFrame);

    return () => {
      if (videoAnimationRef.current) {
        cancelAnimationFrame(videoAnimationRef.current);
      }
    };
  }, [mediaType, video, isPlaying, renderPreview]);

  useEffect(() => {
    if (renderTimeoutRef.current) {
      clearTimeout(renderTimeoutRef.current);
    }
    if (rafRef.current) {
      cancelAnimationFrame(rafRef.current);
    }

    if (previewQuality === 'draft') {
      rafRef.current = requestAnimationFrame(() => {
        renderPreview();
      });
    } else {
      renderTimeoutRef.current = setTimeout(() => {
        rafRef.current = requestAnimationFrame(() => {
          renderPreview();
        });
      }, 150);
    }

    return () => {
      if (renderTimeoutRef.current) {
        clearTimeout(renderTimeoutRef.current);
      }
      if (rafRef.current) {
        cancelAnimationFrame(rafRef.current);
      }
    };
  }, [renderPreview, previewQuality]);

  const lastOverlayParamsRef = useRef(null);

  useEffect(() => {
    const loadOverlayTextures = async () => {
      if (!rendererRef.current) return;

      const overlayEffect = effects.find(e => e.type === 'overlay' && e.enabled);
      if (!overlayEffect) {
        if (lastOverlayParamsRef.current !== null) {
          rendererRef.current.setOverlayTexture(null);
          lastOverlayParamsRef.current = null;
        }
        return;
      }

      const { texture, customTextureUrl } = overlayEffect.params;
      const paramsKey = `${texture}:${customTextureUrl || ''}`;

      if (lastOverlayParamsRef.current === paramsKey) {
        return;
      }

      if (texture === OVERLAY_TEXTURES.CUSTOM && customTextureUrl) {
        try {
          const result = await loadImageFromURL(customTextureUrl);
          if (cancelled) return;
          rendererRef.current.setOverlayTexture(result.image);
          lastOverlayParamsRef.current = paramsKey;
          renderPreview();
        } catch {
          console.warn('Failed to load custom overlay texture');
        }
      } else if (texture !== OVERLAY_TEXTURES.CUSTOM) {
        const textureImg = await generateProceduralTexture(texture);
        if (cancelled) return;
        rendererRef.current.setOverlayTexture(textureImg);
        lastOverlayParamsRef.current = paramsKey;
        renderPreview();
      }
    };

    let cancelled = false;
    loadOverlayTextures();
    return () => {
      cancelled = true;
    };
  }, [effects, renderPreview]);

  const handleImageLoad = useCallback(async (source, type) => {
    if (videoRef.current) {
      videoRef.current.pause();
      if (videoRef.current.src && videoRef.current.src.startsWith('blob:')) {
        URL.revokeObjectURL(videoRef.current.src);
      }
      videoRef.current.src = '';
      videoRef.current = null;
    }

    try {
      let result;

      if (type === 'file') {
        result = await loadImageFromFile(source);
      } else {
        result = await loadImageFromURL(source);
      }

      if (rendererRef.current) {
        rendererRef.current.setImage(result.image);
      }

      setState(s => ({
        ...s,
        image: result.image,
        imageUrl: result.url,
        video: null,
        videoUrl: null,
        mediaType: 'image',
        corsError: result.corsError,
        isPlaying: false,
        currentTime: 0,
        duration: 0
      }));

      if (result.corsError) {
        toaster.create({
          title: 'CORS Warning',
          description: 'Image loaded but export/copy disabled. Re-upload locally to enable.',
          type: 'warning',
          duration: 5000
        });
      }
    } catch (err) {
      toaster.create({
        title: 'Failed to load image',
        description: err.message,
        type: 'error',
        duration: 3000
      });
    }
  }, []);

  const handleVideoLoad = useCallback(async (source, type) => {
    if (videoRef.current) {
      videoRef.current.pause();
      if (videoRef.current.src && videoRef.current.src.startsWith('blob:')) {
        URL.revokeObjectURL(videoRef.current.src);
      }
      videoRef.current.src = '';
    }

    try {
      let result;

      if (type === 'file') {
        result = await loadVideoFromFile(source);
      } else {
        result = await loadVideoFromURL(source);
      }

      videoRef.current = result.video;

      if (rendererRef.current) {
        rendererRef.current.setImage(result.video);
        rendererRef.current.updateVideoFrame(result.video);
        rendererRef.current.render([], 0, result.video.videoWidth, result.video.videoHeight);
      }

      result.video.play();

      setState(s => ({
        ...s,
        image: null,
        imageUrl: null,
        video: result.video,
        videoUrl: result.url,
        mediaType: 'video',
        corsError: result.corsError,
        isPlaying: true,
        currentTime: 0,
        duration: result.duration
      }));

      if (result.corsError) {
        toaster.create({
          title: 'CORS Warning',
          description: 'Video loaded but export disabled. Re-upload locally to enable.',
          type: 'warning',
          duration: 5000
        });
      }
    } catch (err) {
      toaster.create({
        title: 'Failed to load video',
        description: err.message,
        type: 'error',
        duration: 3000
      });
    }
  }, []);

  const handleMediaLoad = useCallback(
    async (source, type) => {
      if (type === 'file') {
        if (source.type.startsWith('video/')) {
          return handleVideoLoad(source, type);
        }
        return handleImageLoad(source, type);
      }

      const ext = source.split('?')[0].split('.').pop()?.toLowerCase();
      if (['mp4', 'webm', 'ogg', 'mov'].includes(ext)) {
        return handleVideoLoad(source, type);
      }
      return handleImageLoad(source, type);
    },
    [handleImageLoad, handleVideoLoad]
  );

  const handlePlayPause = useCallback(() => {
    if (!videoRef.current) return;

    if (videoRef.current.paused) {
      videoRef.current.play();
      setState(s => ({ ...s, isPlaying: true }));
    } else {
      videoRef.current.pause();
      setState(s => ({ ...s, isPlaying: false }));
    }
  }, []);

  const handleSeek = useCallback(
    time => {
      if (!videoRef.current) return;
      videoRef.current.currentTime = time;
      setState(s => ({ ...s, currentTime: time }));
      renderPreview();
    },
    [renderPreview]
  );

  const handleEffectsChange = useCallback(newEffects => {
    setState(s => ({ ...s, effects: newEffects }));
  }, []);

  const handleSeedChange = useCallback(newSeed => {
    setState(s => ({ ...s, seed: newSeed }));
  }, []);

  const handlePreviewQualityChange = useCallback(quality => {
    setState(s => ({ ...s, previewQuality: quality }));
  }, []);

  const handleExportFormatChange = useCallback(format => {
    setState(s => ({ ...s, exportFormat: format }));
  }, []);

  const handleExportQualityChange = useCallback(quality => {
    setState(s => ({ ...s, exportQuality: quality }));
  }, []);

  const handleExportScaleChange = useCallback(scale => {
    setState(s => ({ ...s, exportScale: scale }));
  }, []);

  const handleViewModeChange = useCallback(mode => {
    setState(s => ({ ...s, viewMode: mode }));
  }, []);

  const handleCopyToClipboard = useCallback(async () => {
    if (!canvasRef.current || corsError) return;
    if (mediaType === 'video') {
      toaster.create({
        title: 'Cannot copy video',
        description: 'Copy to clipboard only works for images. Use export for video.',
        type: 'warning',
        duration: 3000
      });
      return;
    }

    try {
      if (rendererRef.current && image) {
        rendererRef.current.render(effects, seed, image.width, image.height);
      }

      if (isClipboardImageSupported()) {
        await copyCanvasToClipboard(canvasRef.current);
        toaster.create({
          title: 'Copied to clipboard',
          type: 'success',
          duration: 2000
        });
      } else {
        await copyDataURLToClipboard(canvasRef.current);
        toaster.create({
          title: 'Copied as data URL',
          description: 'Image clipboard not supported in this browser.',
          type: 'info',
          duration: 3000
        });
      }

      renderPreview();
    } catch (err) {
      toaster.create({
        title: 'Copy failed',
        description: err.message,
        type: 'error',
        duration: 3000
      });
    }
  }, [corsError, mediaType, image, effects, seed, renderPreview]);

  const handleVideoExport = useCallback(async () => {
    if (!canvasRef.current || !video || corsError || isExporting) return;

    setState(s => ({
      ...s,
      isExporting: true,
      exportProgress: 0,
      exportStatus: 'Preparing...'
    }));

    const wasPlaying = !video.paused;
    if (wasPlaying) {
      video.pause();
      setState(s => ({ ...s, isPlaying: false }));
    }

    const wasLooping = video.loop;
    video.loop = false;

    const currentVideo = video;
    const currentCanvas = canvasRef.current;
    const currentRenderer = rendererRef.current;

    let animationId = null;
    let mediaRecorder = null;

    try {
      const videoDuration = currentVideo.duration;

      currentVideo.currentTime = 0;
      await Promise.race([
        new Promise(resolve => {
          const onSeeked = () => {
            currentVideo.removeEventListener('seeked', onSeeked);
            resolve();
          };
          currentVideo.addEventListener('seeked', onSeeked);
        }),
        new Promise(resolve => setTimeout(resolve, 200))
      ]);

      if (currentVideo.currentTime > 0.1) {
        currentVideo.currentTime = 0;
        await new Promise(resolve => setTimeout(resolve, 100));
      }

      currentRenderer.updateVideoFrame(currentVideo);
      currentRenderer.render(effects, seed, currentVideo.videoWidth, currentVideo.videoHeight);

      setState(s => ({ ...s, exportStatus: 'Recording...' }));

      const stream = currentCanvas.captureStream(60);
      mediaRecorder = new MediaRecorder(stream, {
        mimeType: 'video/webm;codecs=vp9',
        videoBitsPerSecond: 12000000
      });

      const chunks = [];
      mediaRecorder.ondataavailable = e => {
        if (e.data.size > 0) chunks.push(e.data);
      };

      const recordingDone = new Promise(resolve => {
        mediaRecorder.onstop = () => resolve(chunks);
      });

      mediaRecorder.start(100);
      await new Promise(resolve => setTimeout(resolve, 50));

      const renderLoop = () => {
        currentRenderer.updateVideoFrame(currentVideo);
        currentRenderer.render(effects, seed, currentVideo.videoWidth, currentVideo.videoHeight);

        const progress = Math.round((currentVideo.currentTime / videoDuration) * 100);
        setState(s => ({
          ...s,
          exportProgress: Math.min(progress, 100),
          exportStatus: 'Exporting...'
        }));

        if (!currentVideo.ended && !currentVideo.paused) {
          animationId = requestAnimationFrame(renderLoop);
        }
      };

      animationId = requestAnimationFrame(renderLoop);

      await currentVideo.play();

      await new Promise(resolve => {
        if (currentVideo.ended) {
          resolve();
          return;
        }
        currentVideo.addEventListener('ended', resolve, { once: true });
      });

      if (animationId) {
        cancelAnimationFrame(animationId);
        animationId = null;
      }

      currentRenderer.updateVideoFrame(currentVideo);
      currentRenderer.render(effects, seed, currentVideo.videoWidth, currentVideo.videoHeight);

      await new Promise(resolve => setTimeout(resolve, 200));

      mediaRecorder.stop();

      const finalChunks = await recordingDone;

      const webmBlob = new Blob(finalChunks, { type: 'video/webm' });
      const url = URL.createObjectURL(webmBlob);
      const link = document.createElement('a');
      link.href = url;
      link.download = `texture-lab-${Date.now()}.webm`;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      URL.revokeObjectURL(url);

      currentVideo.loop = wasLooping;
      currentVideo.currentTime = 0;
      if (wasPlaying) {
        currentVideo.play();
        setState(s => ({ ...s, isPlaying: true }));
      }
      renderPreview();

      setState(s => ({ ...s, isExporting: false, exportProgress: 0, exportStatus: '' }));
      toaster.create({
        title: 'Video exported',
        description: `Saved as WebM (${(webmBlob.size / 1024 / 1024).toFixed(1)} MB)`,
        type: 'success',
        duration: 2000
      });
    } catch (err) {
      console.error('Video export error:', err);

      if (animationId) cancelAnimationFrame(animationId);
      if (mediaRecorder && mediaRecorder.state === 'recording') {
        mediaRecorder.stop();
      }

      currentVideo.loop = wasLooping;
      currentVideo.pause();
      currentVideo.currentTime = 0;

      setState(s => ({ ...s, isExporting: false, exportProgress: 0, exportStatus: '' }));
      toaster.create({
        title: 'Video export failed',
        description: err.message,
        type: 'error',
        duration: 3000
      });
    }
  }, [video, corsError, isExporting, effects, seed, renderPreview]);

  const handleExport = useCallback(async () => {
    if (!canvasRef.current || corsError) return;

    if (mediaType === 'video' && video) {
      return handleVideoExport();
    }

    if (!image) return;

    try {
      const exportWidth = Math.min(image.width * exportScale, 8192);
      const exportHeight = Math.min(image.height * exportScale, 8192);

      if (rendererRef.current) {
        rendererRef.current.render(effects, seed, exportWidth, exportHeight);
      }

      await exportCanvas(canvasRef.current, {
        format: exportFormat,
        quality: exportQuality,
        filename: `texture-lab-${Date.now()}`
      });

      toaster.create({
        title: 'Export complete',
        type: 'success',
        duration: 2000
      });

      renderPreview();
    } catch (err) {
      toaster.create({
        title: 'Export failed',
        description: err.message,
        type: 'error',
        duration: 3000
      });
    }
  }, [
    image,
    video,
    mediaType,
    corsError,
    exportScale,
    exportFormat,
    exportQuality,
    effects,
    seed,
    renderPreview,
    handleVideoExport
  ]);

  const handleReset = useCallback(() => {
    setState(s => ({
      ...s,
      effects: [],
      seed: Math.floor(Math.random() * 100000),
      previewQuality: 'draft',
      viewMode: 'preview',
      exportFormat: 'png',
      exportQuality: 0.92,
      exportScale: 1
    }));
    if (rendererRef.current) {
      rendererRef.current.setOverlayTexture(null);
    }
  }, []);

  const handleClearMedia = useCallback(() => {
    if (videoRef.current) {
      videoRef.current.pause();
      if (videoRef.current.src) {
        URL.revokeObjectURL(videoRef.current.src);
      }
      videoRef.current.src = '';
      videoRef.current = null;
    }

    setState(s => ({
      ...s,
      image: null,
      imageUrl: null,
      video: null,
      videoUrl: null,
      mediaType: null,
      corsError: false,
      isPlaying: false,
      currentTime: 0,
      duration: 0
    }));
    if (rendererRef.current) {
      rendererRef.current.setImage(null);
    }
  }, []);

  return (
    <Flex
      h="100%"
      w="100%"
      gap={{ base: 0, lg: 4 }}
      direction={{ base: 'column', lg: 'row' }}
      position="relative"
      overflow="hidden"
    >
      <Box
        w="280px"
        flexShrink={0}
        h="100%"
        overflow="hidden"
        display={{ base: 'none', lg: 'flex' }}
        flexDirection="column"
      >
        <Controls
          image={image}
          video={video}
          mediaType={mediaType}
          corsError={corsError}
          effects={effects}
          seed={seed}
          previewQuality={previewQuality}
          exportFormat={exportFormat}
          exportQuality={exportQuality}
          exportScale={exportScale}
          isExporting={isExporting}
          exportProgress={exportProgress}
          exportStatus={exportStatus}
          onMediaLoad={handleMediaLoad}
          onClearMedia={handleClearMedia}
          onEffectsChange={handleEffectsChange}
          onSeedChange={handleSeedChange}
          onPreviewQualityChange={handlePreviewQualityChange}
          onExportFormatChange={handleExportFormatChange}
          onExportQualityChange={handleExportQualityChange}
          onExportScaleChange={handleExportScaleChange}
          onCopyToClipboard={handleCopyToClipboard}
          onExport={handleExport}
          onReset={handleReset}
          toolSelector={toolSelector}
        />
      </Box>

      <Box
        flex={1}
        position="relative"
        borderRadius={{ base: '12px', lg: '16px' }}
        overflow="hidden"
        border="1px solid var(--border-primary)"
        bg="var(--bg-body)"
        maxWidth="1920px"
        margin="0 auto"
        width="100%"
        minH={{ base: '400px', lg: 'auto' }}
      >
        <Canvas
          image={image}
          video={video}
          mediaType={mediaType}
          canvasRef={canvasRef}
          viewMode={viewMode}
          onViewModeChange={handleViewModeChange}
          onMediaDrop={file => handleMediaLoad(file, 'file')}
          onMediaLoad={handleMediaLoad}
          isPlaying={isPlaying}
          currentTime={currentTime}
          duration={duration}
          onPlayPause={handlePlayPause}
          onSeek={handleSeek}
          isExporting={isExporting}
        />

        <Flex
          as="button"
          display={{ base: 'flex', lg: 'none' }}
          position="absolute"
          bottom={20}
          right={4}
          align="center"
          gap={2}
          bg="var(--color-primary)"
          px={4}
          py={2.5}
          borderRadius="12px"
          cursor="pointer"
          onClick={() => setMobileControlsOpen(true)}
          boxShadow="0 4px 20px rgba(168, 85, 247, 0.4)"
          _active={{ transform: 'scale(0.95)' }}
          transition="transform 0.1s"
          zIndex={10}
        >
          <Icon as={Settings} boxSize={4} color="#fff" />
          <Text fontSize="13px" fontWeight={600} color="#fff">
            Controls
          </Text>
        </Flex>
      </Box>

      {isMobile && (
        <>
          <Box
            position="fixed"
            top={0}
            left={0}
            right={0}
            bottom={0}
            bg="rgba(0, 0, 0, 0.6)"
            opacity={mobileControlsOpen ? 1 : 0}
            visibility={mobileControlsOpen ? 'visible' : 'hidden'}
            transition="all 0.3s"
            zIndex={999}
            onClick={() => setMobileControlsOpen(false)}
          />

          <Box
            position="fixed"
            bottom={0}
            left={0}
            right={0}
            bg="var(--bg-card)"
            borderTop="1px solid var(--border-primary)"
            borderTopRadius="24px"
            transform={mobileControlsOpen ? 'translateY(0)' : 'translateY(100%)'}
            transition="transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)"
            zIndex={1000}
            maxH="85vh"
            overflow="hidden"
            display="flex"
            flexDirection="column"
          >
            <Flex justify="center" pt={3} pb={2} cursor="pointer" onClick={() => setMobileControlsOpen(false)}>
              <Box w="40px" h="4px" bg="var(--border-primary)" borderRadius="2px" />
            </Flex>

            <Flex align="center" justify="space-between" px={4} pb={3} borderBottom="1px solid var(--border-primary)">
              <Text fontSize="16px" fontWeight={700} color="var(--text-primary)">
                Controls
              </Text>
              <Flex
                as="button"
                align="center"
                justify="center"
                w={8}
                h={8}
                borderRadius="8px"
                bg="var(--bg-elevated)"
                cursor="pointer"
                onClick={() => setMobileControlsOpen(false)}
                _hover={{ bg: 'var(--bg-elevated)' }}
              >
                <Icon as={ChevronUp} boxSize={5} color="var(--text-muted)" />
              </Flex>
            </Flex>

            <Box
              flex={1}
              overflowY="auto"
              px={4}
              py={4}
              css={{
                '&::-webkit-scrollbar': { display: 'none' },
                scrollbarWidth: 'none'
              }}
            >
              <Controls
                image={image}
                video={video}
                mediaType={mediaType}
                corsError={corsError}
                effects={effects}
                seed={seed}
                previewQuality={previewQuality}
                exportFormat={exportFormat}
                exportQuality={exportQuality}
                exportScale={exportScale}
                isExporting={isExporting}
                exportProgress={exportProgress}
                exportStatus={exportStatus}
                onMediaLoad={handleMediaLoad}
                onClearMedia={handleClearMedia}
                onEffectsChange={handleEffectsChange}
                onSeedChange={handleSeedChange}
                onPreviewQualityChange={handlePreviewQualityChange}
                onExportFormatChange={handleExportFormatChange}
                onExportQualityChange={handleExportQualityChange}
                onExportScaleChange={handleExportScaleChange}
                onCopyToClipboard={handleCopyToClipboard}
                onExport={handleExport}
                onReset={handleReset}
                toolSelector={toolSelector}
              />
            </Box>
          </Box>
        </>
      )}
    </Flex>
  );
}