import {
  InspectorControls,
  useBlockProps,
  BlockControls,
  MediaPlaceholder
} from '@wordpress/block-editor';
import {
  PanelBody,
  RangeControl,
  ToolbarGroup,
  ToolbarButton,
  ToggleControl,
  TextControl,
  TextareaControl,
  SelectControl,
  Popover,
  Button,
} from '@wordpress/components';

// @ts-ignore - LinkControl is experimental
import { __experimentalLinkControl as LinkControl } from '@wordpress/block-editor';
import { ColorPickerNullable } from './ColorPickerNullable';
import { __ } from '@wordpress/i18n';
import { useRef, useEffect, useState } from '@wordpress/element';
import { link as linkIcon } from '@wordpress/icons';
import { CameraState } from '../../types/model-viewer';
import { ModelViewer } from '../../model-viewer/ModelViewer';


// type definition for the block attributes
export interface ModelBlockAttributes {
  modelUrl?: string;
  mimeType?: string;
  cameraState?: CameraState | undefined;
  draggable?: boolean;
  zoom?: number;
  width?: number;
  height?: number;
  color?: string;
  shinyMode?: boolean;
  shinyIntensity?: number;
  lightIntensity?: number;
  autoRotate?: boolean;
  autoRotateSpeed?: number;
  autoRotateX?: boolean;
  autoRotateY?: boolean;
  widthValue?: number;
  widthUnit?: 'px' | 'em' | 'rem' | 'vh' | 'vw' | '%';
  heightValue?: number;
  heightUnit?: 'px' | 'em' | 'rem' | 'vh' | 'vw' | '%';
  pluginVersion?: string;
  linkUrl?: string;
  linkOpenInNewTab?: boolean;
  altText?: string;
  enableZoom?: boolean;
  enableRotation?: boolean;
  enablePan?: boolean;
  enableShadows?: boolean;
}

// type for the props passed to the Edit component
interface EditProps {
  attributes: ModelBlockAttributes;
  setAttributes: (attrs: Partial<ModelBlockAttributes>) => void;
  clientId: string;
}

/**
 * Edit component for the 3D model block.
 */
export default function Edit({ attributes, setAttributes, clientId }: EditProps) {
  const {
    modelUrl,
    cameraState = undefined,
    zoom = (window as any).Press3D?.defaultZoom ?? 0.7,
    lightIntensity = 1.5,
    color = undefined, // Let ModelViewer apply default color from settings for STL/OBJ
    shinyMode = false,
    shinyIntensity = 0,
    autoRotate = false,
    autoRotateSpeed = 2,
    autoRotateX = false,
    autoRotateY = true,
    widthValue = (window as any).Press3D?.defaultWidthValue ?? 100,
    widthUnit = (window as any).Press3D?.defaultWidthUnit ?? '%',
    heightValue = (window as any).Press3D?.defaultHeightValue ?? 300,
    heightUnit = (window as any).Press3D?.defaultHeightUnit ?? 'px',
    pluginVersion = (window as any).Press3D?.version,
    linkUrl = '',
    linkOpenInNewTab = false,
    altText = '',
    enableZoom = false,
    enableRotation = false,
    enablePan = false,
    enableShadows = false,
    mimeType,
  } = attributes;

  // Check if model is STL or OBJ based on file extension from URL
  const isStlOrObj = modelUrl ? /\.(stl|obj)$/i.test(modelUrl) : false;
  // Disable shininess for STL/OBJ if no color is selected
  const isShinyDisabled = isStlOrObj && !color;

  const modelRef = useRef<HTMLDivElement | null>(null);
  const viewerInstance = useRef<ModelViewer | null>(null);
  const linkButtonRef = useRef<HTMLButtonElement | null>(null);
  const [dragDisabled, setDragDisabled] = useState(true);
  const [isLinkPopoverOpen, setIsLinkPopoverOpen] = useState(false);

  // Inicializace / update STLViewer
  useEffect(() => {
    if (!modelUrl || !modelRef.current) return;

    if (viewerInstance.current) {
      viewerInstance.current.destroy();
      viewerInstance.current = null;
    }

    try {
      if (!modelUrl) return;

      // If we have a blob URL (common during upload) but no MIME type,
      // we cannot safely detect the loader. Better to wait for the upload to complete
      // and provide the full URL/MIME than to crash detection.
      const isBlob = modelUrl.startsWith('blob:');
      const hasExtension = /\.(stl|obj|glb|gltf)$/i.test(modelUrl);

      if (isBlob && !mimeType) {
        // Waiting for upload completion...
        return;
      }

      // Additional safety: if not blob but no extension and no mime, also wait or fail gracefully
      if (!isBlob && !hasExtension && !mimeType) {
        return;
      }

      viewerInstance.current = new ModelViewer(modelRef.current, {
        modelUrl: modelUrl,
        cameraState: cameraState,
        zoom: zoom,
        lightIntensity: lightIntensity,
        mimeType: mimeType,
        // Only pass color if explicitly set, otherwise let ModelViewer apply default for STL/OBJ
        ...(color !== undefined && { color: color }),
        shinyIntensity: shinyIntensity,
        autoRotate: autoRotate,
        autoRotateSpeed: autoRotateSpeed,
        autoRotateX: autoRotateX,
        autoRotateY: autoRotateY,
        enableRotate: true,  // Editor always controllable
        enableZoom: true,    // Editor always controllable
        enablePan: true,     // Editor always controllable
        widthValue: widthValue,
        widthUnit: widthUnit,
        heightValue: heightValue,
        heightUnit: heightUnit,
        pluginVersion: pluginVersion,
        showOrbitWidget: true,
        orbitWidgetOptions: {
          position: 'top-left'
        },
        enableShadows: enableShadows
      });

      viewerInstance.current.loadAsync().then(() => {
        // After model loads, sync color attribute with viewer's actual color
        // (which may be default color from settings for STL/OBJ)
        if (viewerInstance.current && !color) {
          const actualColor = viewerInstance.current.getColor();
          if (actualColor) {
            setAttributes({ color: actualColor });
          }
        }
      });
    } catch (e) {
      console.error("Failed to initialize Press3D viewer:", e);
    }

    // uložit kameru a zoom po interakci
    if (viewerInstance.current) {
      viewerInstance.current.getControls().addEventListener('end', () => {
        if (!viewerInstance.current) return;
        setAttributes({
          cameraState: viewerInstance.current.getCameraState(),
          zoom: viewerInstance.current.getZoom(),
        });
      });
    }

    return () => {
      viewerInstance.current?.destroy();
      viewerInstance.current = null;
    };
  }, [modelUrl]);

  // Nastavení dragování
  useEffect(() => {
    if (!modelRef.current) return;

    const blockEl = modelRef.current.closest('.block-editor-block-list__block');
    if (!blockEl) return;

    if (dragDisabled) {
      blockEl.setAttribute('draggable', 'false');
    } else {
      blockEl.setAttribute('draggable', 'true');
    }
  }, [dragDisabled, modelUrl]);

  // Update šířky a výšky
  useEffect(() => {
    if (!viewerInstance.current) return;
    if (widthValue && widthUnit) {
      viewerInstance.current.setWidth(`${widthValue}${widthUnit}`);
    }
    if (heightValue && heightUnit) {
      viewerInstance.current.setHeight(`${heightValue}${heightUnit}`);
    }
  }, [widthValue, widthUnit, heightValue, heightUnit]);

  // Update barvy - only if color is explicitly set
  useEffect(() => {
    if (!viewerInstance.current) return;
    // Only update if color is explicitly set (not undefined)
    // This prevents overriding default color from settings
    if (color !== undefined) {
      viewerInstance.current.setColor(color);
    }
  }, [color]);

  // Update shiny intensity
  useEffect(() => {
    viewerInstance.current?.setShinyIntensity(shinyIntensity);
  }, [shinyIntensity]);

  // Reset model on color clear
  useEffect(() => {
    const handleColorClear = async (e: any) => {
      // Only respond if the event was triggered by this specific block
      if (e.detail?.clientId !== clientId) return;

      if (!viewerInstance.current) return;
      await viewerInstance.current.resetColor();
    };

    document.addEventListener('press3d:colorClear', handleColorClear);
    return () => document.removeEventListener('press3d:colorClear', handleColorClear);
  }, [clientId]);

  // Update intenzity svetla
  useEffect(() => {
    viewerInstance.current?.setLightIntensity(lightIntensity);
  }, [lightIntensity]);

  // Update autorotace
  useEffect(() => {
    viewerInstance.current?.setAutoRotate(autoRotate, autoRotateSpeed);
  }, [autoRotate, autoRotateSpeed]);

  // Update autorotate osy
  useEffect(() => {
    viewerInstance.current?.setAutoRotateAxes(autoRotateX, autoRotateY);
  }, [autoRotateX, autoRotateY]);

  // Update camera controls (stored for frontend, not applied to editor viewer)
  useEffect(() => {
    if (!viewerInstance.current) return;
    viewerInstance.current.setEnableZoom(enableZoom);
    viewerInstance.current.setEnableRotation(enableRotation);
    viewerInstance.current.setEnablePan(enablePan);
  }, [enableZoom, enableRotation, enablePan]);

  // Update shadows
  useEffect(() => {
    if (!viewerInstance.current) return;
    viewerInstance.current.setShadows(enableShadows);
  }, [enableShadows]);

  const blockProps = useBlockProps({
    //className: `press3d-block1`
  });

  return (
    <div {...blockProps}>
      <BlockControls>
        <ToolbarGroup>
          <ToolbarButton
            icon="lock"
            label={__('Disable block dragging', 'press3d')}
            isPressed={dragDisabled}
            onClick={() => setDragDisabled((prev) => !prev)}
          />
        </ToolbarGroup>
        <ToolbarGroup>
          <ToolbarButton
            ref={linkButtonRef}
            icon={linkIcon}
            label={__('Link', 'press3d')}
            onClick={() => setIsLinkPopoverOpen(!isLinkPopoverOpen)}
            isPressed={isLinkPopoverOpen || !!linkUrl}
          />
          {isLinkPopoverOpen && (
            <Popover
              position="bottom center"
              onClose={() => setIsLinkPopoverOpen(false)}
              anchor={linkButtonRef.current}
              focusOnMount="firstElement"
            >
              <div style={{ padding: '16px' }}>
                <LinkControl
                  value={linkUrl ? {
                    url: linkUrl,
                    opensInNewTab: linkOpenInNewTab
                  } : undefined}
                  onChange={(newValue: any) => {
                    let newUrl = newValue?.url || '';
                    
                    // Trim whitespace to prevent bypass with leading/trailing spaces
                    newUrl = newUrl.trim();
                    
                    // Block dangerous protocols (javascript:, data:, vbscript:, file:, etc.)
                    // Allow only safe protocols: http, https, mailto, tel, and relative URLs
                    const dangerousProtocols = /^(javascript|data|vbscript|file):/i;
                    const safeProtocols = /^(https?:\/\/|mailto:|tel:|\/|#)/i;
                    
                    if (dangerousProtocols.test(newUrl) || (newUrl && !safeProtocols.test(newUrl))) {
                      newUrl = '';
                    }
                    
                    setAttributes({
                      linkUrl: newUrl,
                      linkOpenInNewTab: newValue?.opensInNewTab || false
                    });
                  }}
                  settings={[
                    {
                      id: 'opensInNewTab',
                      title: __('Open in new tab', 'press3d')
                    }
                  ]}
                />
                {linkUrl && (
                  <Button
                    variant="tertiary"
                    onClick={() => {
                      setAttributes({ linkUrl: '', linkOpenInNewTab: false });
                      setIsLinkPopoverOpen(false);
                    }}
                    style={{ marginTop: '8px', width: '100%' }}
                  >
                    {__('Remove link', 'press3d')}
                  </Button>
                )}
              </div>
            </Popover>
          )}
        </ToolbarGroup>
      </BlockControls>

      <InspectorControls>

        {/* Dimension */}
        <PanelBody title={__('Dimension', 'press3d')} initialOpen={false}>
          <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
            <div style={{ flex: '1' }}>
              <TextControl
                label={__('Width', 'press3d')}
                type="number"
                min={0}
                step={1}
                value={widthValue}
                onChange={(val: string) => setAttributes({ widthValue: Number(val) })}
              />
            </div>
            <div style={{ flex: '1' }}>
              <SelectControl
                label={__('Unit', 'press3d')}
                value={widthUnit}
                options={[
                  { label: 'px', value: 'px' },
                  { label: '%', value: '%' },
                  { label: 'em', value: 'em' },
                  { label: 'rem', value: 'rem' },
                  { label: 'vw', value: 'vw' },
                  { label: 'vh', value: 'vh' },
                ]}
                onChange={(val) => setAttributes({ widthUnit: val })}
              />
            </div>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
            <div style={{ flex: '1' }}>
              <TextControl
                label={__('Height', 'press3d')}
                type="number"
                min={0}
                step={1}
                value={heightValue}
                onChange={(val: string) => setAttributes({ heightValue: Number(val) })}
              />
            </div>
            <div style={{ flex: '1' }}>
              <SelectControl
                label={__('Unit', 'press3d')}
                value={heightUnit}
                options={[
                  { label: 'px', value: 'px' },
                  { label: '%', value: '%' },
                  { label: 'em', value: 'em' },
                  { label: 'rem', value: 'rem' },
                  { label: 'vw', value: 'vw' },
                  { label: 'vh', value: 'vh' },
                ]}
                onChange={(val) => setAttributes({ heightUnit: val })}
              />
            </div>
          </div>
        </PanelBody>

        {/* Animation */}
        <PanelBody title={__('Animation', 'press3d')} initialOpen={false}>
          <ToggleControl
            label={__('Enable Auto-Rotate', 'press3d')}
            checked={autoRotate}
            onChange={(value) => setAttributes({ autoRotate: value })}
          />
          {autoRotate && (
            <div style={{ marginBottom: '16px' }}>
              <label style={{ display: 'block', fontWeight: 500, marginBottom: '8px' }}>
                {__('Rotation axes', 'press3d')}
              </label>
              <div style={{ display: 'flex', gap: '16px', marginBottom: '16px' }}>
                <ToggleControl
                  label="X"
                  checked={autoRotateX}
                  onChange={(value) => setAttributes({ autoRotateX: value })}
                />
                <ToggleControl
                  label="Y"
                  checked={autoRotateY}
                  onChange={(value) => setAttributes({ autoRotateY: value })}
                />
              </div>

              <label style={{ display: 'block', fontWeight: 500, marginBottom: '4px' }}>
                {__('Rotate speed', 'press3d')}
              </label>
              <input
                type="range"
                min="-10"
                max="10"
                step="0.1"
                value={autoRotateSpeed}
                onChange={(e) => {
                  const val = parseFloat(e.target.value);
                  setAttributes({ autoRotateSpeed: isNaN(val) ? 2 : val });
                }}
                style={{ width: '100%' }}
              />
              <div
                style={{
                  display: 'flex',
                  justifyContent: 'space-between',
                  fontSize: '11px',
                  opacity: 0.7,
                  marginTop: '-4px',
                }}
              >
                <span>-10</span>
                <span>0</span>
                <span>10</span>
              </div>
            </div>
          )}
        </PanelBody>

        {/* Appearance */}
        <PanelBody title={__('Appearance', 'press3d')} initialOpen={false}>
          <RangeControl
            label={__('Light intensity', 'press3d')}
            value={lightIntensity}
            onChange={(value) => setAttributes({ lightIntensity: value })}
            min={0}
            max={10}
            step={0.1}
          />

          <div style={{
            display: 'grid',
            gridTemplateColumns: '1fr 1fr',
            gap: '12px',
            padding: '12px',
            background: '#f6f7f7',
            border: '1px solid #dcdcde',
            borderRadius: '4px',
            marginBottom: '16px'
          }}>
            <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
              <label style={{
                fontWeight: 600,
                fontSize: '11px',
                textTransform: 'uppercase',
                color: '#50575e',
                letterSpacing: '0.5px'
              }}>
                {__('Color', 'press3d')}
              </label>
              <ColorPickerNullable
                value={color ?? null}
                onChange={(v) => setAttributes({ color: v === null ? undefined : v })}
                clientId={clientId}
              />
            </div>

            <div style={{
              display: 'flex',
              flexDirection: 'column',
              gap: '8px',
              opacity: isShinyDisabled ? 0.4 : 1,
              pointerEvents: isShinyDisabled ? 'none' : 'auto',
              cursor: isShinyDisabled ? 'not-allowed' : 'default',
              transition: 'opacity 0.2s ease'
            }}
              title={isShinyDisabled ? __('Available only when a color is selected for this model type.', 'press3d') : ''}
            >
              <label style={{
                fontWeight: 600,
                fontSize: '11px',
                textTransform: 'uppercase',
                color: '#50575e',
                letterSpacing: '0.5px'
              }}>
                {__('Shininess', 'press3d')}
              </label>
              <RangeControl
                value={shinyIntensity ?? 0}
                onChange={(value) => setAttributes({ shinyIntensity: value })}
                min={0}
                max={1}
                step={0.01}
                withInputField={false}
                trackColor="#c9c9c9"
                renderTooltipContent={(value) => `${Math.round((value ? value : 0) * 100)}%`}
              />
            </div>
          </div>

          <ToggleControl
            label={__('Enable Shadows', 'press3d')}
            checked={enableShadows}
            onChange={(value) => setAttributes({ enableShadows: value })}
          />
        </PanelBody>

        {/* Camera Controls */}
        <PanelBody title={__('Camera Controls', 'press3d')} initialOpen={false}>
          <ToggleControl
            label={__('Enable Zoom', 'press3d')}
            checked={enableZoom}
            onChange={(value) => setAttributes({ enableZoom: value })}
            help={__('Allow users to zoom in/out on the frontend.', 'press3d')}
          />
          <ToggleControl
            label={__('Enable Rotation', 'press3d')}
            checked={enableRotation}
            onChange={(value) => setAttributes({ enableRotation: value })}
            help={__('Allow users to rotate the model on the frontend.', 'press3d')}
          />
          <ToggleControl
            label={__('Enable Pan', 'press3d')}
            checked={enablePan}
            onChange={(value) => setAttributes({ enablePan: value })}
            help={__('Allow users to pan the camera on the frontend.', 'press3d')}
          />
        </PanelBody>

        {/* Accessibility */}
        <PanelBody title={__('Accessibility', 'press3d')} initialOpen={false}>
          <TextareaControl
            label={__('Alternative Text', 'press3d')}
            value={altText}
            onChange={(value) => setAttributes({ altText: value })}
            help={__('Describe the 3D model for screen readers.', 'press3d')}
          />
        </PanelBody>

      </InspectorControls>

      {modelUrl ? (
        <div ref={modelRef} style={{ width: '100%', height: '100%' }} />
      ) : (
        <MediaPlaceholder
          className="press3d-block1-media-placeholder"
          icon="format-image"
          labels={{
            title: __('3D Model', 'press3d'),
            instructions: __('Upload a 3D file (STL, OBJ, GLB, or GLTF), or choose one from your library.', 'press3d'),
          }}
          accept="model/stl,application/vnd.ms-pki.stl,model/gltf+json,model/gltf-binary,model/obj,application/octet-stream,.obj,.stl,.glb,.gltf"
          allowedTypes={[
            'model/stl',
            'application/vnd.ms-pki.stl',
            'model/gltf+json',
            'model/gltf-binary',
            'model/obj',
            'application/octet-stream'
          ]}
          onSelect={(media) => {
            let mime = media.mime_type || media.mime;

            // Collect all possible sources of the filename to find extension
            const candidates = [
              media.filename,
              media.name,
              media.title,
              media.url
            ].filter(s => typeof s === 'string');

            const hasExt = (ext: string) => candidates.some(c => c.toLowerCase().endsWith('.' + ext));

            // Normalize MIME/type if browser/WP detection was generic
            if (hasExt('stl')) mime = 'model/stl';
            else if (hasExt('obj')) mime = 'model/obj';
            else if (hasExt('glb')) mime = 'model/glb';
            else if (hasExt('gltf')) mime = 'model/gltf';

            setAttributes({ modelUrl: media.url, mimeType: mime });
          }}
        />
      )}
    </div>
  );
}
