import { Layer } from "ol/layer";
import { Deck } from "@deck.gl/core";
import { toLonLat } from "ol/proj";

/**
 * 自定义 Layer 类来包装 deck.gl
 * 将 deck.gl 图层集成到 OpenLayers 地图中
 */
export class DeckLayer extends Layer {
  private deck: Deck;
  private canvasEl: HTMLCanvasElement | null;
  private isDisposed = false;
  private onVisibilityChange?: (layerId: string, visible: boolean) => void;
  private userVisibility: Map<string, boolean> = new Map(); // 存储用户设置的可见性

  /**
   * @param deckInstance deck.gl 实例
   * @param layerProperties 可选：在构造时直接设置到 OpenLayers Layer 的 properties
   */
  constructor(
    deckInstance: Deck,
    canvasElement: HTMLCanvasElement | null,
    layerProperties?: Record<string, any>
  ) {
    // 将 layerProperties 作为 Layer 的 properties 传入
    super({ properties: layerProperties || {} });
    this.deck = deckInstance;
    this.canvasEl = canvasElement;
    // 再次确保属性应用到实例（兼容场景）
    if (layerProperties) {
      this.setProperties(layerProperties);
    }
  }

  // 设置可见性变化回调
  setVisibilityChangeCallback(
    callback: (layerId: string, visible: boolean) => void
  ): void {
    this.onVisibilityChange = callback;
  }

  // 初始化用户可见性状态
  initUserVisibility(layerId: string, visible: boolean): void {
    this.userVisibility.set(layerId, visible);
  }

  render(frameState: any): HTMLElement {
    if (this.isDisposed) {
      return this.canvasEl || document.createElement("div");
    }
    const { size, viewState } = frameState;
    const [width, height] = size;
    const [longitude, latitude] = toLonLat(viewState.center);
    const zoom = viewState.zoom - 1; // 调整 zoom 以匹配
    const bearing = (-viewState.rotation * 180) / Math.PI;
    const deckViewState = { bearing, longitude, latitude, zoom };
    this.deck.setProps({ width, height, viewState: deckViewState });
    this.deck.redraw();
    return this.canvasEl || document.createElement("div");
  }

  // 获取 Deck 实例
  getDeck(): Deck {
    return this.deck;
  }

  // 设置图层
  setDeckLayers(layers: any[]): void {
    if (this.isDisposed) return;
    this.deck.setProps({ layers });
  }

  resetSessionLayers(): void {
    if (this.isDisposed) return;
    this.userVisibility.clear();
    this.deck.setProps({ layers: [] });
  }

  // 获取当前图层
  getDeckLayers(): any[] {
    if (this.isDisposed) return [];
    return this.deck.props.layers || [];
  }

  // 添加图层
  addDeckLayer(layer: any): void {
    if (this.isDisposed) return;
    const currentLayers = this.getDeckLayers();
    // 如果已有同 id 图层，则替换保持顺序；否则追加
    const idx = currentLayers.findIndex((l: any) => l && l.id === layer.id);
    if (idx >= 0) {
      const copy = [...currentLayers];
      copy[idx] = layer;
      this.deck.setProps({ layers: copy });
      return;
    }
    this.deck.setProps({ layers: [...currentLayers, layer] });
  }

  // 移除图层
  removeDeckLayer(layerId: string): void {
    if (this.isDisposed) return;
    const currentLayers = this.getDeckLayers();
    const filteredLayers = currentLayers.filter(
      (layer: any) => layer && layer.id !== layerId
    );
    this.deck.setProps({ layers: filteredLayers });
  }

  // 根据 ID 查找图层
  getDeckLayerById(layerId: string): any | undefined {
    const layers = this.getDeckLayers();
    return layers.find((layer: any) => layer && layer.id === layerId);
  }

  // 更新特定图层：支持传入一个新的 Layer 实例或一个 props 对象
  // - 如果传入的是 Layer 实例，则直接替换同 id 的图层为该实例
  // - 如果传入的是 props（普通对象），则基于原图层调用 clone(props)
  updateDeckLayer(layerId: string, layerOrProps: any): void {
    if (this.isDisposed) return;
    const layers = this.getDeckLayers();
    const updatedLayers = layers.map((layer: any) => {
      if (!layer || layer.id !== layerId) return layer;

      // 如果传入的是一个 deck.gl Layer 实例（通常包含 id 和 props）
      if (
        layerOrProps &&
        typeof layerOrProps === "object" &&
        layerOrProps.id !== undefined &&
        typeof layerOrProps.clone === "function"
      ) {
        // 替换为新的 layer 实例
        return layerOrProps;
      }

      if (layerOrProps && typeof layer.clone === "function") {
        // 传入 props 时，基于原图层 clone，避免丢失类型
        return layer.clone(layerOrProps);
      }

      return layer;
    });

    this.deck.setProps({ layers: updatedLayers });
  }

  // 获取图层的可见性（用户设置的状态，不受其他条件影响）
  getDeckLayerVisible(layerId: string): boolean | undefined {
    // 优先返回用户设置的可见性
    if (this.userVisibility.has(layerId)) {
      return this.userVisibility.get(layerId);
    }
    // 如果没有用户设置，返回实际图层的可见性
    const layer = this.getDeckLayerById(layerId);
    return layer ? layer.props.visible : undefined;
  }

  // 设置图层的可见性
  setDeckLayerVisible(layerId: string, visible: boolean): void {
    // 存储用户设置的可见性
    this.userVisibility.set(layerId, visible);
    // 更新图层（注意：实际的 visible 可能还受其他条件控制）
    // 优先尝试使用传入的 layer 实例替换，否则使用 clone({ visible }) 来保留图层类型
    const found = this.getDeckLayerById(layerId);
    if (!found) return;
    try {
      // 使用 clone 来确保保持同类型实例
      this.updateDeckLayer(layerId, { visible });
    } catch (err) {
      // 降级：直接替换属性
      this.updateDeckLayer(layerId, { visible });
    }
    // 触发回调通知外部
    if (this.onVisibilityChange) {
      this.onVisibilityChange(layerId, visible);
    }
  }

  // 切换图层的可见性
  toggleDeckLayerVisible(layerId: string): void {
    const currentVisible = this.getDeckLayerVisible(layerId);
    if (currentVisible !== undefined) {
      this.setDeckLayerVisible(layerId, !currentVisible);
    }
  }

  // 可选的封装：在外部用更语义化的方式设置属性（内部可直接调用 setProperties）
  setLayerProperties(props: Record<string, any>): void {
    this.setProperties(props);
  }

  isDisposedLayer(): boolean {
    return this.isDisposed;
  }

  disposeDeck(): void {
    if (this.isDisposed) return;
    this.isDisposed = true;
    this.onVisibilityChange = undefined;
    this.userVisibility.clear();
    try {
      this.deck.setProps({ layers: [] });
    } catch (error) {
      console.warn("Clear deck layers failed", error);
    }
    try {
      this.deck.finalize();
    } catch (error) {
      console.warn("Finalize deck failed", error);
    }
    this.canvasEl = null;
  }
}
