/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

import { useCallback, useEffect, useState, useRef } from 'react';
import {
  Home,
  ZoomIn,
  ZoomOut,
  Layers,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { useViewerStore } from '@/store';
import { goHomeFromStore } from '@/store/homeView';
import { useIfc } from '@/hooks/useIfc';
import { cn } from '@/lib/utils';
import { ViewCube, type ViewCubeRef } from './ViewCube';
import { AxisHelper, type AxisHelperRef } from './AxisHelper';
import { BasepointOverlay } from './BasepointOverlay';
import { PointCloudPanel } from './PointCloudPanel';
import { Crosshair } from 'lucide-react';

export function ViewportOverlays({ hideViewCube = false }: { hideViewCube?: boolean } = {}) {
  const selectedStoreys = useViewerStore((s) => s.selectedStoreys);
  const hiddenEntities = useViewerStore((s) => s.hiddenEntities);
  const isolatedEntities = useViewerStore((s) => s.isolatedEntities);
  const basketPresentationVisible = useViewerStore((s) => s.basketPresentationVisible);
  const cameraCallbacks = useViewerStore((s) => s.cameraCallbacks);
  const isMobile = useViewerStore((s) => s.isMobile);
  const setOnCameraRotationChange = useViewerStore((s) => s.setOnCameraRotationChange);
  const setOnScaleChange = useViewerStore((s) => s.setOnScaleChange);
  const { ifcDataStore, geometryResult } = useIfc();

  // Cesium state
  const cesiumEnabled = useViewerStore((s) => s.cesiumEnabled);

  // Use refs for rotation to avoid re-renders - ViewCube updates itself directly
  const cameraRotationRef = useRef({ azimuth: 45, elevation: 25 });
  const viewCubeRef = useRef<ViewCubeRef | null>(null);
  const axisHelperRef = useRef<AxisHelperRef | null>(null);

  // Local state for scale - updated via callback, no global re-renders
  const [scale, setScale] = useState(10);
  const lastScaleRef = useRef(10);

  // Register callback for real-time rotation updates - updates ViewCube directly
  useEffect(() => {
    const handleRotationChange = (rotation: { azimuth: number; elevation: number }) => {
      cameraRotationRef.current = rotation;
      // Update ViewCube directly via ref (no React re-render)
      const viewCubeRotationX = -rotation.elevation;
      const viewCubeRotationY = -rotation.azimuth;
      viewCubeRef.current?.updateRotation(viewCubeRotationX, viewCubeRotationY);
      axisHelperRef.current?.updateRotation(viewCubeRotationX, viewCubeRotationY);
    };
    setOnCameraRotationChange(handleRotationChange);
    return () => setOnCameraRotationChange(null);
  }, [setOnCameraRotationChange]);

  // Register callback for real-time scale updates
  // Only update state if scale changed significantly (>1%) to avoid unnecessary re-renders
  useEffect(() => {
    const handleScaleChange = (newScale: number) => {
      const lastScale = lastScaleRef.current;
      // Only update if scale changed by more than 1%
      if (Math.abs(newScale - lastScale) / lastScale > 0.01) {
        lastScaleRef.current = newScale;
        setScale(newScale);
      }
    };
    setOnScaleChange(handleScaleChange);
    return () => setOnScaleChange(null);
  }, [setOnScaleChange]);

  // Get names of selected storeys
  const storeyNames = selectedStoreys.size > 0 && ifcDataStore
    ? Array.from(selectedStoreys).map(id => 
        ifcDataStore.entities.getName(id) || `Storey #${id}`
      )
    : null;

  // Calculate visible count considering visibility filters
  const totalCount = geometryResult?.meshes?.length ?? 0;
  let visibleCount = totalCount;
  if (isolatedEntities !== null) {
    visibleCount = isolatedEntities.size;
  } else if (hiddenEntities.size > 0) {
    visibleCount = totalCount - hiddenEntities.size;
  }

  // Initial rotation values (ViewCube will update itself via ref)
  const initialRotationX = -cameraRotationRef.current.elevation;
  const initialRotationY = -cameraRotationRef.current.azimuth;

  const handleViewChange = useCallback((view: string) => {
    const viewMap: Record<string, 'top' | 'bottom' | 'front' | 'back' | 'left' | 'right'> = {
      top: 'top',
      bottom: 'bottom',
      front: 'front',
      back: 'back',
      left: 'left',
      right: 'right',
    };
    const mappedView = viewMap[view];
    if (mappedView && cameraCallbacks.setPresetView) {
      cameraCallbacks.setPresetView(mappedView);
    }
  }, [cameraCallbacks]);

  const handleHome = useCallback(() => {
    goHomeFromStore();
  }, []);

  const handleFitAll = useCallback(() => {
    cameraCallbacks.fitAll?.();
  }, [cameraCallbacks]);

  const handleZoomIn = useCallback(() => {
    cameraCallbacks.zoomIn?.();
  }, [cameraCallbacks]);

  const handleZoomOut = useCallback(() => {
    cameraCallbacks.zoomOut?.();
  }, [cameraCallbacks]);

  // Format scale value for display
  const formatScale = (worldSize: number): string => {
    if (worldSize >= 1000) {
      return `${(worldSize / 1000).toFixed(1)}km`;
    } else if (worldSize >= 1) {
      return `${worldSize.toFixed(1)}m`;
    } else if (worldSize >= 0.1) {
      return `${(worldSize * 100).toFixed(0)}cm`;
    } else {
      return `${(worldSize * 1000).toFixed(0)}mm`;
    }
  };

  return (
    <>
      <PointCloudPanelMount />
      {/* Bottom-right: Navigation controls (hidden when Cesium active — Cesium is web-only) */}
      {!cesiumEnabled && (
        <div
          className={cn(
            'absolute flex flex-col gap-1 bg-background/90 backdrop-blur-sm border p-1',
            // Mobile: bottom-left at ~15% up from lower edge — thumb-reachable on
            // portrait phones and well clear of the URL bar. Tight radii + flat
            // background match the codebase's brutalist panel-chrome vocabulary.
            isMobile ? 'left-4 bottom-[15%] rounded-md' : 'bottom-4 right-4 rounded-lg shadow-sm',
          )}
        >
          <Tooltip>
            <TooltipTrigger asChild>
              <Button variant="ghost" size="icon-sm" className={cn(isMobile && 'min-h-[44px] min-w-[44px]')} onClick={handleHome}>
                <Home className={cn(isMobile ? 'h-5 w-5' : 'h-4 w-4')} />
              </Button>
            </TooltipTrigger>
            <TooltipContent side="left">Home (H)</TooltipContent>
          </Tooltip>

          <Tooltip>
            <TooltipTrigger asChild>
              <Button variant="ghost" size="icon-sm" className={cn(isMobile && 'min-h-[44px] min-w-[44px]')} onClick={handleZoomIn}>
                <ZoomIn className={cn(isMobile ? 'h-5 w-5' : 'h-4 w-4')} />
              </Button>
            </TooltipTrigger>
            <TooltipContent side="left">Zoom In (+)</TooltipContent>
          </Tooltip>

          <Tooltip>
            <TooltipTrigger asChild>
              <Button variant="ghost" size="icon-sm" className={cn(isMobile && 'min-h-[44px] min-w-[44px]')} onClick={handleZoomOut}>
                <ZoomOut className={cn(isMobile ? 'h-5 w-5' : 'h-4 w-4')} />
              </Button>
            </TooltipTrigger>
            <TooltipContent side="left">Zoom Out (-)</TooltipContent>
          </Tooltip>
        </div>
      )}

      {/* Context Info — Storey names. Top-center on mobile (URL bar steals the bottom). */}
      {storeyNames && storeyNames.length > 0 && (
        <div className={cn(
          'absolute left-1/2 -translate-x-1/2 px-4 py-2 bg-background/80 backdrop-blur-sm rounded-full border shadow-sm',
          isMobile ? 'top-4' : basketPresentationVisible ? 'bottom-28' : 'bottom-4',
        )}>
          <div className="flex items-center gap-2 text-sm">
            <Layers className="h-4 w-4 text-primary" />
            <span className="font-medium">
              {storeyNames.length === 1
                ? storeyNames[0]
                : `${storeyNames.length} storeys`}
            </span>
          </div>
        </div>
      )}

      {/* ViewCube (top-right) */}
      {!hideViewCube && (
        <div className="absolute top-6 right-6">
          <ViewCube
            ref={viewCubeRef}
            onViewChange={handleViewChange}
            onDrag={(deltaX, deltaY) => cameraCallbacks.orbit?.(deltaX, deltaY)}
            rotationX={initialRotationX}
            rotationY={initialRotationY}
          />
        </div>
      )}

      {/* Basepoint toggle + Axis Helper + Scale Bar — desktop only; mobile keeps the viewport unobstructed */}
      {!isMobile && (
        <div className="absolute bottom-4 left-4 flex flex-col-reverse items-start gap-3">
          <div className="flex flex-col items-start gap-1">
            <div className="h-1 w-24 bg-foreground/80 rounded-full" />
            <span className="text-xs text-foreground/80">{formatScale(scale)}</span>
          </div>
          <AxisHelper
            ref={axisHelperRef}
            rotationX={initialRotationX}
            rotationY={initialRotationY}
          />
          <BasepointToggleButton />
        </div>
      )}

      {/* Per-model IFC (0,0,0) markers — toggled via BasepointToggleButton.
          Hidden by default; component returns null when the toggle is off. */}
      <BasepointOverlay />
    </>
  );
}

/**
 * Toggle for the per-model IFC-origin overlay. Sits next to the AxisHelper so
 * it's discoverable in the same "scene reference" cluster.
 */
function BasepointToggleButton() {
  const showModelBasepoints = useViewerStore((s) => s.showModelBasepoints);
  const toggleShowModelBasepoints = useViewerStore((s) => s.toggleShowModelBasepoints);
  const modelCount = useViewerStore((s) => s.models.size);
  if (modelCount === 0) return null;
  return (
    <Tooltip>
      <TooltipTrigger asChild>
        <button
          type="button"
          onClick={toggleShowModelBasepoints}
          aria-label={showModelBasepoints ? 'Hide model basepoints' : 'Show model basepoints'}
          className={cn(
            'h-6 w-6 inline-flex items-center justify-center border transition-colors',
            showModelBasepoints
              ? 'border-amber-500 bg-amber-50 dark:bg-amber-950/40 text-amber-700 dark:text-amber-300'
              : 'border-zinc-300 dark:border-zinc-700 bg-white/80 dark:bg-zinc-900/80 text-zinc-600 dark:text-zinc-300 hover:bg-zinc-50 dark:hover:bg-zinc-800',
          )}
          aria-pressed={showModelBasepoints}
        >
          <Crosshair className="h-3 w-3" />
        </button>
      </TooltipTrigger>
      <TooltipContent side="top" className="text-xs">
        {showModelBasepoints ? 'Hide model basepoints' : 'Show model basepoints (IFC 0,0,0)'}
      </TooltipContent>
    </Tooltip>
  );
}


/**
 * Tiny indirection so the panel can subscribe to its own slice without
 * pulling extra state into the parent overlay component.
 */
function PointCloudPanelMount() {
  const count = useViewerStore((s) => s.pointCloudAssetCount);
  // Triangle total comes from the merged geometry result. The panel
  // gates the BIM↔scan deviation compute button on triangleCount > 0
  // so the user can't trigger an empty-BVH compute pass.
  const triangleCount = useViewerStore((s) => s.geometryResult?.totalTriangles ?? 0);
  return <PointCloudPanel assetCount={count} triangleCount={triangleCount} />;
}
