import { Canvas } from "@react-three/fiber";
import { Experience } from "./components/Experience";
import { WelcomeUI } from "./components/WelcomeUI";
import SiteCursor from "./components/SiteCursor";
import ColorfulAsciiOverlay from "./components/ColorfulAsciiOverlay";
import { useState, useEffect, Suspense, useRef, useCallback } from "react";
import { preloadSingleImage } from "./utils/projectThumbnailPreloader";
import { pickRandomWndrboiFact } from "./constants/wndrboiFacts";
import { clearWndrboiDialogScreenPosition } from "./stores/wndrboiDialogPositionStore";
import './components/global.css';
import './components/Experience.css';

const INTRO_BRAND_HOLD_MS = 850;
const INTRO_TYPE_START_MS = 160;
const INTRO_TYPE_CHAR_MS = 9;
const INTRO_TYPE_LINE_PAUSE_MS = 55;
const INTRO_AFTER_TYPE_PAUSE_MS = 420;
const INTRO_SCENE_FALLBACK_MS = 1800;
const INTRO_SCENE_FALLBACK_MOBILE_MS = 3200;
const INTRO_LOGO_HOLD_MS = 1100;
const INTRO_SAFETY_DURATION_MS = 8000;
const INTRO_REPEAT_LOGO_HOLD_MS = 1250;
const INTRO_REPEAT_SAFETY_DURATION_MS = 2800;
const INTRO_CODE_SEEN_STORAGE_KEY = 'asxIntroCodeSeen:v4';

const introSequenceLines = [
  '[ASX://INIT]',
  '<ANFASLIM>.creative.init()',
  'title = multidisciplinary_creative',
  'roles = [visual_artist, designer, animator]',
  'media.mount(illustration, motion, 3D, web)',
  'base.set(Chicago, IL)',
  'render(welcome_ui)',
];

const buildIntroTypingTimeline = () => {
  const updates = [];
  let time = INTRO_BRAND_HOLD_MS + INTRO_TYPE_START_MS;

  introSequenceLines.forEach((line, lineIndex) => {
    for (let charIndex = 1; charIndex <= line.length; charIndex += 1) {
      updates.push({
        at: time,
        lineIndex,
        text: line.slice(0, charIndex),
      });
      time += INTRO_TYPE_CHAR_MS;
    }
    time += INTRO_TYPE_LINE_PAUSE_MS;
  });

  return {
    updates,
    logoAt: time + INTRO_AFTER_TYPE_PAUSE_MS,
  };
};

const introTypingTimeline = buildIntroTypingTimeline();

const hasSeenIntroCodeSequence = () => {
  if (typeof window === 'undefined') return false;

  try {
    return window.localStorage.getItem(INTRO_CODE_SEEN_STORAGE_KEY) === 'true';
  } catch {
    return false;
  }
};

const markIntroCodeSequenceSeen = () => {
  try {
    window.localStorage.setItem(INTRO_CODE_SEEN_STORAGE_KEY, 'true');
  } catch {
    // localStorage may be unavailable in private browsing modes.
  }
};

function App() {
  const [showMenu, setShowMenu] = useState(false);
  const [isInteractive, setIsInteractive] = useState(false);
  const [isMobile, setIsMobile] = useState(false);
  const [showAbout, setShowAbout] = useState(false);
  const [showContact, setShowContact] = useState(false);
  const [showHomeView, setShowHomeView] = useState(false);
  const [showIntro, setShowIntro] = useState(true);
  const [sceneReady, setSceneReady] = useState(false);
  const [skipIntroCode] = useState(hasSeenIntroCodeSequence);
  const [introPhase, setIntroPhase] = useState(() => (hasSeenIntroCodeSequence() ? 'reload' : 'brand'));
  const [typedIntroLines, setTypedIntroLines] = useState(() => introSequenceLines.map(() => ''));
  const [activeIntroLine, setActiveIntroLine] = useState(0);
  const [introMinimumElapsed, setIntroMinimumElapsed] = useState(false);
  const [viewportHeight, setViewportHeight] = useState('100vh');
  const [devicePerformance, setDevicePerformance] = useState('high');
  const [showCheckoutConfirm, setShowCheckoutConfirm] = useState(false);
  const [wndrboiDialog, setWndrboiDialog] = useState(null);
  const lastWndrboiFactRef = useRef(null);
  const wndrboiDialogTimerRef = useRef(0);
  const initialCameraPosition = [0, 2, 5];
  const shouldRenderScene = skipIntroCode || introPhase === 'logo' || introPhase === 'reload' || !showIntro;

  const clearWndrboiDialog = useCallback(() => {
    window.clearTimeout(wndrboiDialogTimerRef.current);
    setWndrboiDialog(null);
    clearWndrboiDialogScreenPosition();
  }, []);

  const handleWndrboiClick = useCallback(() => {
    if (!showHomeView || !isInteractive) return;

    window.clearTimeout(wndrboiDialogTimerRef.current);

    const fact = pickRandomWndrboiFact(lastWndrboiFactRef.current);
    lastWndrboiFactRef.current = fact;
    setWndrboiDialog({ type: 'fact', text: fact });

    wndrboiDialogTimerRef.current = window.setTimeout(clearWndrboiDialog, 7000);
  }, [showHomeView, isInteractive, clearWndrboiDialog]);

  useEffect(() => {
    if (showHomeView) return undefined;

    lastWndrboiFactRef.current = null;
    clearWndrboiDialog();

    return () => window.clearTimeout(wndrboiDialogTimerRef.current);
  }, [showHomeView, clearWndrboiDialog]);

  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const sessionId = params.get('session_id');
    const fromStripeSession = sessionId?.startsWith('cs_');
    const fromCheckoutParam = params.get('checkout') === 'success';

    let fromPendingCheckout = false;
    try {
      const pending = sessionStorage.getItem('asxStripeCheckout');
      if (pending) {
        fromPendingCheckout = Date.now() - Number(pending) < 2 * 60 * 60 * 1000;
        sessionStorage.removeItem('asxStripeCheckout');
      }
    } catch {
      // sessionStorage unavailable
    }

    if (fromStripeSession || fromCheckoutParam || fromPendingCheckout) {
      const isMobileCheckoutReturn = window.innerWidth <= 768;

      setShowCheckoutConfirm(!isMobileCheckoutReturn);
      setShowIntro(false);
      setShowMenu(false);
      setShowHomeView(false);
      setIsInteractive(false);
      window.history.replaceState({}, '', window.location.pathname);
    }
  }, []);

  useEffect(() => {
    setIsInteractive(showHomeView || !showMenu);
    setIsMobile(window.innerWidth <= 768);

    // Detect device performance capabilities
    const detectPerformance = () => {
      const memory = navigator.deviceMemory || 4;
      const cores = navigator.hardwareConcurrency || 4;
      const connection = navigator.connection;
      
      let performance = 'high';
      if (memory < 2 || cores < 2 || (connection && connection.effectiveType === 'slow-2g')) {
        performance = 'low';
      } else if (memory < 4 || cores < 4 || (connection && connection.effectiveType === '2g')) {
        performance = 'medium';
      }

      // Cap at medium tier — same canvas/scene budget as mobile on all devices.
      if (performance === 'high') {
        performance = 'medium';
      }
      
      setDevicePerformance(performance);
  
    };

    const handleResize = () => {
      setIsMobile(window.innerWidth <= 768);
      updateViewportHeight();
    };

    const updateViewportHeight = () => {
      if (isMobile) {
        // Use actual viewport height for mobile
        const vh = window.innerHeight * 0.01;
        document.documentElement.style.setProperty('--vh', `${vh}px`);
        setViewportHeight(`${window.innerHeight}px`);
      } else {
        setViewportHeight('100vh');
      }
    };

    // Initial setup
    updateViewportHeight();
    detectPerformance();

    // Handle mobile viewport changes (address bar show/hide)
    if (isMobile) {
      window.addEventListener('resize', handleResize);
      window.addEventListener('orientationchange', handleResize);
      
      // Handle visual viewport changes
      if (window.visualViewport) {
        window.visualViewport.addEventListener('resize', updateViewportHeight);
      }
    }

    window.addEventListener('resize', handleResize);
    return () => {
      window.removeEventListener('resize', handleResize);
      if (isMobile) {
        window.removeEventListener('orientationchange', handleResize);
        if (window.visualViewport) {
          window.visualViewport.removeEventListener('resize', updateViewportHeight);
        }
      }
    };
  }, [showMenu, showHomeView, isMobile]);

  useEffect(() => {
    if (skipIntroCode) {
      setTypedIntroLines(introSequenceLines.map(() => ''));
      setActiveIntroLine(introSequenceLines.length);
      return undefined;
    }

    let rafId = 0;
    let cancelled = false;
    const startTime = performance.now();
    let lastUpdateIdx = -1;
    let currentPhase = 'brand';

    setTypedIntroLines(introSequenceLines.map(() => ''));
    setActiveIntroLine(0);
    setIntroPhase('brand');

    const setPhase = (phase) => {
      if (currentPhase === phase) return;
      currentPhase = phase;
      setIntroPhase(phase);
    };

    const tick = (now) => {
      if (cancelled) return;

      const elapsed = now - startTime;

      if (elapsed < INTRO_BRAND_HOLD_MS) {
        setPhase('brand');
        rafId = requestAnimationFrame(tick);
        return;
      }

      if (elapsed >= introTypingTimeline.logoAt) {
        setPhase('logo');
        setActiveIntroLine(introSequenceLines.length);
        setTypedIntroLines(introSequenceLines.map((line) => line));
        markIntroCodeSequenceSeen();
        return;
      }

      setPhase('sequence');

      let updateIdx = -1;
      for (let i = introTypingTimeline.updates.length - 1; i >= 0; i -= 1) {
        if (introTypingTimeline.updates[i].at <= elapsed) {
          updateIdx = i;
          break;
        }
      }

      if (updateIdx !== lastUpdateIdx && updateIdx >= 0) {
        lastUpdateIdx = updateIdx;
        const { lineIndex, text } = introTypingTimeline.updates[updateIdx];
        setActiveIntroLine(lineIndex);
        setTypedIntroLines((previousLines) => {
          const nextLines = [...previousLines];
          nextLines[lineIndex] = text;
          return nextLines;
        });
      }

      rafId = requestAnimationFrame(tick);
    };

    rafId = requestAnimationFrame(tick);

    return () => {
      cancelled = true;
      cancelAnimationFrame(rafId);
    };
  }, [skipIntroCode]);

  useEffect(() => {
    if (introPhase !== 'logo' && introPhase !== 'reload') return undefined;

    const minimumTimer = setTimeout(
      () => setIntroMinimumElapsed(true),
      skipIntroCode ? INTRO_REPEAT_LOGO_HOLD_MS : INTRO_LOGO_HOLD_MS
    );

    return () => {
      clearTimeout(minimumTimer);
    };
  }, [introPhase, skipIntroCode]);

  useEffect(() => {
    if (!showIntro) return undefined;
    if (!skipIntroCode && introPhase !== 'logo' && introPhase !== 'reload') return undefined;

    const safetyTimer = window.setTimeout(
      () => setShowIntro(false),
      skipIntroCode ? INTRO_REPEAT_SAFETY_DURATION_MS : 12000
    );

    return () => window.clearTimeout(safetyTimer);
  }, [showIntro, skipIntroCode, introPhase]);

  useEffect(() => {
    if (shouldRenderScene) return;
    setSceneReady(false);
  }, [shouldRenderScene]);

  useEffect(() => {
    if (!introMinimumElapsed) return undefined;

    if (sceneReady) {
      setShowIntro(false);
      return undefined;
    }

    const fallbackMs = INTRO_SCENE_FALLBACK_MOBILE_MS;
    const fallbackTimer = window.setTimeout(() => setShowIntro(false), fallbackMs);
    return () => window.clearTimeout(fallbackTimer);
  }, [sceneReady, introMinimumElapsed, isMobile]);


  useEffect(() => {
    if (!shouldRenderScene) return;
    preloadSingleImage('/textures/texture.jpg', { fetchPriority: 'high', useLinkPreload: true });
  }, [shouldRenderScene]);

  useEffect(() => {
    if (showIntro) return undefined;

    const protectedMediaSelector = 'img, video, picture, canvas, svg';
    const isProtectedMedia = (target) => target instanceof Element && target.closest(protectedMediaSelector);
    const preventMediaAction = (event) => {
      if (isProtectedMedia(event.target)) {
        event.preventDefault();
      }
    };

    const protectMediaElements = () => {
      document.querySelectorAll('img, video').forEach((element) => {
        element.setAttribute('draggable', 'false');

        if (element.tagName.toLowerCase() === 'video') {
          element.setAttribute('controlsList', 'nodownload noplaybackrate noremoteplayback');
          element.setAttribute('disablePictureInPicture', '');
          element.setAttribute('disableRemotePlayback', '');
        }
      });
    };

    protectMediaElements();
    const observer = new MutationObserver(protectMediaElements);
    observer.observe(document.body, { childList: true, subtree: true });

    document.addEventListener('contextmenu', preventMediaAction, true);
    document.addEventListener('dragstart', preventMediaAction, true);
    document.addEventListener('copy', preventMediaAction, true);

    return () => {
      observer.disconnect();
      document.removeEventListener('contextmenu', preventMediaAction, true);
      document.removeEventListener('dragstart', preventMediaAction, true);
      document.removeEventListener('copy', preventMediaAction, true);
    };
  }, [showIntro]);

  // Dynamic Canvas settings based on device performance
  const getCanvasSettings = () => {
    const baseSettings = {
      shadows: false,
      camera: { position: initialCameraPosition, fov: isMobile ? 75 : 65 },
      gl: { 
        antialias: false,
        powerPreference: 'high-performance', 
        stencil: false, 
        depth: true, 
        preserveDrawingBuffer: true,
        alpha: false,
        logarithmicDepthBuffer: false
      },
      dpr: isMobile ? 0.75 : showIntro ? [0.875, 1.125] : [1, 1.5],
      performance: { 
        min: isMobile ? 0.5 : 0.7
      },
      frameloop: !showIntro || introPhase === 'logo' || introPhase === 'reload' ? 'always' : 'never',
      style: { 
        width: '100vw', 
        height: viewportHeight, 
        display: 'block',
        margin: 0,
        padding: 0,
        position: 'fixed',
        top: 0,
        left: 0,
        zIndex: 0
      }
    };

    return baseSettings;
  };

  return (
    <>
      {shouldRenderScene && (
        <Canvas {...getCanvasSettings()}>
          <color attach="background" args={["#000000"]} />
          <Suspense fallback={null}>
            <Experience 
              isInteractive={isInteractive} 
              isMobile={isMobile}
              showMenu={showMenu}
              showAbout={showAbout}
              showContact={showContact}
              showHomeView={showHomeView}
              wndrboiDialog={wndrboiDialog}
              devicePerformance={devicePerformance}
              onWndrboiClick={handleWndrboiClick}
              onReady={() => setSceneReady(true)}
            />
          </Suspense>
        </Canvas>
      )}
      {!showIntro && (
        <ColorfulAsciiOverlay active={!showHomeView} isMobile={isMobile} />
      )}
      <div className={`intro-overlay intro-overlay-${introPhase} ${showIntro ? '' : 'hide'}`}>
        <div className="intro-content">
          <div className="intro-code-sequence" aria-hidden={introPhase !== 'sequence'}>
            {introSequenceLines.map((line, index) => {
              const typedLine = typedIntroLines[index] || '';
              const isTypingLine = introPhase === 'sequence' && index === activeIntroLine;
              const isVisibleLine = typedLine.length > 0 || isTypingLine;

              return (
                <span
                  key={`${line}-${index}`}
                  className={`intro-code-line ${isVisibleLine ? 'is-visible' : ''} ${isTypingLine ? 'is-typing' : ''}`}
                  style={{ '--line-index': index }}
                >
                  {typedLine}
                </span>
              );
            })}
          </div>
          <div className="intro-loading-bar" aria-label="Loading">
            <span className="intro-loading-bar-fill" />
          </div>
          <div className="intro-logo-reveal" aria-label="@ASX">
            <img src="/textures/Asx.png" alt="@ASX" className="intro-logo" />
          </div>
        </div>
      </div>
      <WelcomeUI 
        setShowMenu={setShowMenu} 
        showMenu={showMenu} 
        setIsInteractive={setIsInteractive}
        isMobile={isMobile}
        devicePerformance={devicePerformance}
        showIntro={showIntro}
        setShowHomeView={setShowHomeView}
        showHomeView={showHomeView}
        wndrboiDialog={wndrboiDialog}
        showCheckoutConfirm={showCheckoutConfirm}
        onClearCheckoutConfirm={() => setShowCheckoutConfirm(false)}
      />
      {!isMobile && !showIntro && <SiteCursor />}
    </>
  );
}

export default App;