"use client";

import React, { useState, useEffect, useMemo, useRef } from "react";
import Image from "next/image";
import { useData, useMap } from "../MapComponent";
import TileLayer from "ol/layer/Tile.js";
import XYZ from "ol/source/XYZ.js";
import Group from "ol/layer/Group";
import mapboxOutdoors from "@assets/map/layers/mapbox-outdoors.png";
import mapboxLight from "@assets/map/layers/mapbox-light.png";
import mapboxSatellite from "@assets/map/layers/mapbox-satellite.png";
import mapboxSatelliteStreet from "@assets/map/layers/mapbox-satellite-streets.png";
import mapboxStreets from "@assets/map/layers/mapbox-streets.png";
import clsx from "clsx";
import { MAPBOX_TOKEN, TIANDITU_TOKEN } from "@config/config";
import type { Map as OlMap } from "ol";
import { markMapResourcePersistent } from "../mapLifecycle";

const INITIAL_LAYER = "mapbox-light";
const BASE_LAYER_METADATA = [
  {
    id: "mapbox-light",
    name: "默认地图",
    img: mapboxLight.src,
    attribution: "© Mapbox © OpenStreetMap",
  },
  {
    id: "mapbox-satellite",
    name: "卫星地图",
    img: mapboxSatellite.src,
    attribution: "© Mapbox",
  },
  {
    id: "mapbox-satellite-streets",
    name: "卫星街道地图",
    img: mapboxSatelliteStreet.src,
    attribution: "© Mapbox © OpenStreetMap",
  },
  {
    id: "mapbox-streets",
    name: "街道地图",
    img: mapboxStreets.src,
    attribution: "© Mapbox © OpenStreetMap",
  },
  {
    id: "tianditu-vector",
    name: "天地图矢量",
    img: mapboxOutdoors.src,
    attribution: "© 天地图",
  },
  {
    id: "tianditu-image",
    name: "天地图影像",
    img: mapboxSatellite.src,
    attribution: "© 天地图",
  },
] as const;

const createTileSource = (url: string, attributions: string) =>
  new XYZ({
    url,
    crossOrigin: "anonymous",
    tileSize: 512,
    maxZoom: 20,
    projection: "EPSG:3857",
    attributions,
  });

export const createBaseLayerSources = () => ({
  streets: createTileSource(
    `https://api.mapbox.com/styles/v1/mapbox/streets-v12/tiles/256/{z}/{x}/{y}@2x?access_token=${MAPBOX_TOKEN}`,
    '数据来源：<a href="https://www.mapbox.com/">Mapbox</a> & <a href="https://www.openstreetmap.org/">OpenStreetMap</a>',
  ),
  light: createTileSource(
    `https://api.mapbox.com/styles/v1/mapbox/light-v11/tiles/256/{z}/{x}/{y}@2x?access_token=${MAPBOX_TOKEN}`,
    '数据来源：<a href="https://www.mapbox.com/">Mapbox</a> & <a href="https://www.openstreetmap.org/">OpenStreetMap</a>',
  ),
  satellite: createTileSource(
    `https://api.mapbox.com/styles/v1/mapbox/satellite-v9/tiles/256/{z}/{x}/{y}@2x?access_token=${MAPBOX_TOKEN}`,
    '数据来源：<a href="https://www.mapbox.com/">Mapbox</a> & <a href="https://www.openstreetmap.org/">OpenStreetMap</a>',
  ),
  satelliteStreets: createTileSource(
    `https://api.mapbox.com/styles/v1/mapbox/satellite-streets-v12/tiles/256/{z}/{x}/{y}@2x?access_token=${MAPBOX_TOKEN}`,
    '数据来源：<a href="https://www.mapbox.com/">Mapbox</a> & <a href="https://www.openstreetmap.org/">OpenStreetMap</a>',
  ),
  tiandituVector: new XYZ({
    url: `https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TOKEN}`,
    crossOrigin: "anonymous",
    projection: "EPSG:3857",
    attributions: '数据来源：<a href="https://www.tianditu.gov.cn/">天地图</a>',
  }),
  tiandituVectorAnnotation: new XYZ({
    url: `https://t0.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TOKEN}`,
    crossOrigin: "anonymous",
    projection: "EPSG:3857",
    attributions: '数据来源：<a href="https://www.tianditu.gov.cn/">天地图</a>',
  }),
  tiandituImage: new XYZ({
    url: `https://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TOKEN}`,
    crossOrigin: "anonymous",
    projection: "EPSG:3857",
    attributions: '数据来源：<a href="https://www.tianditu.gov.cn/">天地图</a>',
  }),
  tiandituImageAnnotation: new XYZ({
    url: `https://t0.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TOKEN}`,
    crossOrigin: "anonymous",
    projection: "EPSG:3857",
    attributions: '数据来源：<a href="https://www.tianditu.gov.cn/">天地图</a>',
  }),
});

export type BaseLayerSources = ReturnType<typeof createBaseLayerSources>;

export const createBaseLayerEntries = (sources: BaseLayerSources) => {
  const tileLayer = (source: XYZ) => new TileLayer({ source });

  return [
    {
      ...BASE_LAYER_METADATA[0],
      layer: tileLayer(sources.light),
    },
    {
      ...BASE_LAYER_METADATA[1],
      layer: tileLayer(sources.satellite),
    },
    {
      ...BASE_LAYER_METADATA[2],
      layer: tileLayer(sources.satelliteStreets),
    },
    {
      ...BASE_LAYER_METADATA[3],
      layer: tileLayer(sources.streets),
    },
    {
      ...BASE_LAYER_METADATA[4],
      layer: new Group({
        layers: [
          tileLayer(sources.tiandituVector),
          tileLayer(sources.tiandituVectorAnnotation),
        ],
      }),
    },
    {
      ...BASE_LAYER_METADATA[5],
      layer: new Group({
        layers: [
          tileLayer(sources.tiandituImage),
          tileLayer(sources.tiandituImageAnnotation),
        ],
      }),
    },
  ].map((entry) => {
    const layer = markMapResourcePersistent(entry.layer);
    layer.set("exportAttribution", entry.attribution);
    return { ...entry, layer };
  });
};

const BaseLayers: React.FC = () => {
  const map = useMap();
  const data = useData();
  const maps = useMemo(() => {
    if (data?.maps?.length) return data.maps;
    return map ? [map] : [];
  }, [data?.maps, map]);
  const sharedSources = useMemo(() => createBaseLayerSources(), []);
  const layerSetsRef = useRef(
    new WeakMap<OlMap, ReturnType<typeof createBaseLayerEntries>>(),
  );
  const [isShow, setShow] = useState(false);
  const [isExpanded, setExpanded] = useState(false);
  const [activeId, setActiveId] = useState(INITIAL_LAYER);

  useEffect(() => {
    maps.forEach((targetMap) => {
      let layerEntries = layerSetsRef.current.get(targetMap);
      if (!layerEntries) {
        layerEntries = createBaseLayerEntries(sharedSources);
        layerSetsRef.current.set(targetMap, layerEntries);
      }

      layerEntries.forEach((layerInfo) => {
        const layers = targetMap.getLayers().getArray();
        if (!layers.includes(layerInfo.layer)) {
          targetMap.getLayers().insertAt(0, layerInfo.layer);
        }
        layerInfo.layer.setVisible(layerInfo.id === activeId);
      });
    });
  }, [activeId, maps, sharedSources]);

  const changeMapLayers = (id: string) => {
    maps.forEach((targetMap) => {
      const layerEntries = layerSetsRef.current.get(targetMap);
      layerEntries?.forEach(({ id: layerId, layer }) => {
        layer.setVisible(layerId === id);
      });
    });
  };

  const baseLayers = BASE_LAYER_METADATA;

  const handleQuickSwitch = () => {
    const nextId =
      activeId === baseLayers[0].id ? baseLayers[1].id : baseLayers[0].id;
    setActiveId(nextId);
    changeMapLayers(nextId);
  };

  const handleMapLayers = (id: string) => {
    setActiveId(id);
    changeMapLayers(id);
  };

  const hideTimer = React.useRef<NodeJS.Timeout | null>(null);

  const handleEnter = () => {
    if (hideTimer.current) {
      clearTimeout(hideTimer.current);
      hideTimer.current = null;
    }
    setShow(true);
    setExpanded(true);
  };

  const handleLeave = () => {
    setShow(false);
    hideTimer.current = setTimeout(() => {
      setExpanded(false);
    }, 300);
  };

  useEffect(
    () => () => {
      if (hideTimer.current) {
        clearTimeout(hideTimer.current);
      }
    },
    [],
  );

  return (
    <div className="absolute right-17 bottom-11 z-20">
      <div
        className="w-20 h-20 bg-white rounded-xl drop-shadow-xl shadow-black"
        onMouseEnter={handleEnter}
        onMouseLeave={handleLeave}
      >
        <div className="w-20 h-20 p-1">
          <button onClick={() => handleQuickSwitch()}>
            <Image
              width={240}
              height={100}
              src={
                activeId === baseLayers[0].id
                  ? baseLayers[1].img
                  : baseLayers[0].img
              }
              alt={
                activeId === baseLayers[0].id
                  ? baseLayers[1].name
                  : baseLayers[0].name
              }
              sizes="72px"
              className="object-cover object-left w-18 h-18 rounded-xl"
            />
            <div className=" absolute left-1 bottom-1 flex w-18 h-auto items-center justify-center rounded-b-xl text-xs text-white bg-black opacity-80">
              <span>
                {activeId === baseLayers[0].id
                  ? baseLayers[1].name
                  : baseLayers[0].name}
              </span>
            </div>
          </button>
        </div>
      </div>
      {isExpanded && (
        <div
          className={clsx(
            "absolute flex right-24 bottom-0 w-132 h-25 bg-white rounded-xl drop-shadow-xl shadow-black transition-all duration-300",
            isShow ? "opacity-100" : "opacity-0",
          )}
          onMouseEnter={handleEnter}
          onMouseLeave={handleLeave}
        >
          {baseLayers.map((item) => (
            <button
              key={item.id}
              className="flex flex-auto flex-col justify-center items-center text-gray-500 text-xs"
              onClick={() => handleMapLayers(item.id)}
            >
              <Image
                width={240}
                height={100}
                src={item.img}
                alt={item.name}
                sizes="64px"
                className={clsx(
                  "object-cover object-left w-16 h-16 rounded-md border-2 border-white hover:ring-2 ring-blue-300",
                  {
                    "ring-1 ring-blue-300": activeId === item.id,
                  },
                )}
              />
              <span className="pt-1">{item.name}</span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
};

export default BaseLayers;
