import React, { useEffect, useRef, useState } from 'react';
import { useFrame, useThree } from '@react-three/fiber';
import { PerspectiveCamera } from '@react-three/drei';
import { EffectComposer, ToneMapping } from '@react-three/postprocessing';
import { ToneMappingMode } from 'postprocessing';
import * as THREE from 'three';

import { AerialPerspective, Atmosphere } from '@takram/three-atmosphere/r3f';
import { Clouds, CloudLayer } from '@takram/three-clouds/r3f';

import { TilesPlugin, TilesRenderer } from '3d-tiles-renderer/r3f';
import {
  GLTFExtensionsPlugin,
  GoogleCloudAuthPlugin,
  TileCompressionPlugin,
  TilesFadePlugin,
  UpdateOnChangePlugin,
} from '3d-tiles-renderer/plugins';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';

import { FollowCamera } from './controls/CameraRig';
import { worldState } from './worldState';

const TIME_SCALE = 100;
const INITIAL_DATE = new Date('2024-06-21T12:00:00');
const WEATHER_INTERVAL = 5 * 60 * 1000;

const WEATHER_PRESETS = [
  {
    name: 'Clear',
    coverage: 0.05,
    layers: [
      { channel: 'r', altitude: 2000, height: 500, densityScale: 0.05 },
      { channel: 'b', altitude: 7500, height: 500, densityScale: 0.05 },
    ]
  },
  {
    name: 'Sunny',
    coverage: 0.2,
    layers: [
      { channel: 'r', altitude: 1500, height: 500, densityScale: 0.3 },
      { channel: 'b', altitude: 7500, height: 500, densityScale: 0.15 },
    ]
  },
  {
    name: 'Cloudy',
    coverage: 0.4,
    layers: [
      { channel: 'r', altitude: 1500, height: 500, densityScale: 0.4 },
      { channel: 'g', altitude: 2000, height: 800, densityScale: 0.3 },
    ]
  }
];

const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/v1/decoders/');

function GoogleMaps3DTiles() {
  const apiKey = import.meta.env.VITE_GOOGLE_MAP_API_KEY;
  if (!apiKey) return null;

  return (
    <TilesRenderer url={`https://tile.googleapis.com/v1/3dtiles/root.json?key=${apiKey}`}>
      <TilesPlugin plugin={GoogleCloudAuthPlugin} args={{ apiToken: apiKey }} />
      <TilesPlugin plugin={GLTFExtensionsPlugin} dracoLoader={dracoLoader} />
      <TilesPlugin plugin={TileCompressionPlugin} />
      <TilesPlugin plugin={UpdateOnChangePlugin} />
      <TilesPlugin plugin={TilesFadePlugin} />
    </TilesRenderer>
  );
}

export default function AtmosphereScene({ timeScale: timeScaleProp, overrideDate, overrideTimeScale }) {
  const { gl } = useThree();
  const sunRef = useRef();
  const atmosphereRef = useRef();
  const dateRef = useRef(new Date(INITIAL_DATE));
  const savedDateRef = useRef(null);
  const timeScaleRef = useRef(timeScaleProp ?? TIME_SCALE);

  // Override date for burst effect
  const hasBurstRef = useRef(false);
  useEffect(() => {
    if (overrideDate) {
      hasBurstRef.current = true;
      dateRef.current = new Date(overrideDate);
    } else if (hasBurstRef.current) {
      hasBurstRef.current = false;
      // Burst ended: set to sunrise
      dateRef.current = new Date('2024-06-22T10:00:00');
    }
  }, [overrideDate]);

  timeScaleRef.current = overrideTimeScale ?? timeScaleProp ?? TIME_SCALE;
  const [weather, setWeather] = useState(WEATHER_PRESETS[1]);

  useEffect(() => {
    gl.toneMapping = THREE.NoToneMapping;
    gl.toneMappingExposure = 10.0;
  }, [gl]);

  useEffect(() => {
    const interval = setInterval(() => {
      setWeather(prev => {
        const others = WEATHER_PRESETS.filter(w => w.name !== prev.name);
        return others[Math.floor(Math.random() * others.length)];
      });
    }, WEATHER_INTERVAL);
    return () => clearInterval(interval);
  }, []);

  useFrame((_, delta) => {
    const currentDate = dateRef.current;
    currentDate.setTime(currentDate.getTime() + delta * timeScaleRef.current * 1000);
    worldState.currentHour = currentDate.getHours() + currentDate.getMinutes() / 60;

    if (atmosphereRef.current) {
      atmosphereRef.current.updateByDate(currentDate);
      const sunDirection = atmosphereRef.current.sunDirection;
      if (sunRef.current && sunDirection) {
        sunRef.current.position.copy(sunDirection);
        sunRef.current.intensity = sunDirection.y < -0.1 ? 0.1 : 3.0;
      }
    }
  });

  return (
    <>
      <PerspectiveCamera makeDefault near={10} far={10000000} fov={45} />
      <FollowCamera />
      <directionalLight ref={sunRef} position={[0, 1, 0]} intensity={3.0} castShadow />
      <Atmosphere ref={atmosphereRef}>
        <GoogleMaps3DTiles />
        <EffectComposer multisampling={0} disableNormalPass={false}>
          <Clouds disableDefaultLayers coverage={weather.coverage}>
            {weather.layers.map((layer, i) => (
              <CloudLayer key={i} channel={layer.channel} altitude={layer.altitude}
                height={layer.height} densityScale={layer.densityScale} shapeAmount={0.5} />
            ))}
          </Clouds>
          <AerialPerspective sky />
          <ToneMapping mode={ToneMappingMode.AGX} />
        </EffectComposer>
      </Atmosphere>
    </>
  );
}
