import { Mesh, PBRMaterial, Texture, AbstractMesh, Plane, Vector3, Scene, Color3 } from "@babylonjs/core";
import { Observer } from "@babylonjs/core/Misc/observable";
import { Nullable } from "@babylonjs/core/types";
import { Monobehiver } from '../base/Monobehiver';
import { Dictionary } from '../utils/Dictionary';
import { AppConfig } from './AppConfig';

type RollerDoorOptions = {
  /** 目标升起高度，缺省为初始 y + 3 */
  upY?: number;
  /** 落下终点，缺省为初始 y */
  downY?: number;
  /** 运动速度（单位/秒），缺省 1 */
  speed?: number;
  /** 自定义门体网格名列表，不传使用默认两个卷帘门 */
  meshNames?: string[];
};

/**
 * 游戏管理器类 - 负责管理游戏逻辑、材质和纹理
 */
export class GameManager extends Monobehiver {
  private materialDic: Dictionary<PBRMaterial>;
  private meshDic: Dictionary<any>;
  private oldTextureDic: Dictionary<any>;
  private rollerDoorMeshes: AbstractMesh[];
  private rollerDoorGroup: AbstractMesh | null;
  private rollerDoorInitialY: Map<string, number>;
  private rollerDoorObserver: Nullable<Observer<Scene>>;
  private rollerDoorIsOpen: boolean;
  private rollerDoorNames: string[];
  private yClipPlane: Plane | null;
  private yClipTargets: string[] | null;
  private clipPlaneVisualization: Mesh | null;

  // 记录加载失败的贴图 
  private failedTextures: Array<{
    path: string;
    materialName?: string;
    textureType?: string;
    error?: string;
    timestamp: Date;
  }>;

  constructor(mainApp: any) {
    super(mainApp);
    this.materialDic = new Dictionary<PBRMaterial>();
    this.meshDic = new Dictionary<any>();
    this.oldTextureDic = new Dictionary<any>();
    this.rollerDoorMeshes = [];
    this.rollerDoorGroup = null;
    this.rollerDoorInitialY = new Map();
    this.rollerDoorObserver = null;
    this.rollerDoorIsOpen = false;
    this.rollerDoorNames = ["Box006.001", "Box005.001"];
    this.yClipPlane = null;
    this.yClipTargets = null;
    this.clipPlaneVisualization = null;
    this.failedTextures = [];
  }

  /** 调试：返回当前场景中所有网格名称 */
  listMeshNames(): string[] {
    return this.meshDic.Keys();
  }

  /** 初始化游戏管理器 */
  async Awake() {
    const scene = this.mainApp.appScene?.object;
    if (!scene) {
      console.warn('Scene not found');
      return;
    }

    // 初始化材质字典
    for (const mat of scene.materials) {
      if (!this.materialDic.Has(mat.name)) {
        // 初始化材质属性
        // mat.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHABLEND;
        this.materialDic.Set(mat.name, mat as PBRMaterial);
      }
    }

    // 初始化网格字典
    for (const mesh of scene.meshes) {
      if (mesh instanceof Mesh) {

        this.meshDic.Set(mesh.name, mesh);
      }
    }
    this.cacheRollerDoorMeshes();
    console.log('材质字典:', this.materialDic);
    this.setRollerDoorScale("Box006.001",  new Vector3(0.12,0.02,0.118));

    // 单独设置 Box005.001 的缩放为 (1, 2, 1)
    this.setRollerDoorScale("Box005.001", new Vector3(0.13,0.02,0.12));
  }

  /** 初始化设置材质 */
  async initSetMaterial(oldObject: any) {
    if (!oldObject?.Component?.length) return;


    const { degreeId, Component } = oldObject;
    let degreeTextureDic = this.oldTextureDic.Get(degreeId) || {};
    const texturePromises: Promise<void>[] = [];

    // 处理每个组件
    for (const component of Component) {
      const {
        name,
        albedoTexture,
        bumpTexture,
        alphaTexture,
        aoTexture,
      } = component;

      if (!name) continue;

      // 获取材质
      const mat = this.materialDic.Get(name);
      if (!mat) {
        continue;
      }

      // 获取或初始化纹理字典
      const textureDic = degreeTextureDic[name] || {
        albedo: null,
        bump: null,
        alpha: null,
        ao: null
      };

      // 定义纹理任务
      const textureTasks = [
        {
          key: "albedo",
          path: albedoTexture,
          property: "albedoTexture"
        },
        {
          key: "bump",
          path: bumpTexture,
          property: "bumpTexture"
        },
        {
          key: "alpha",
          path: alphaTexture,
          property: "opacityTexture"
        },
        {
          key: "ao",
          path: aoTexture,
          property: "ambientTexture"
        }
      ];

      // 处理每个纹理任务
      for (const task of textureTasks) {
        const { key, path, property } = task;
        if (!path) continue;

        const fullPath = this.getPublicUrl() + path;
        let texture = textureDic[key];

        if (!texture) {
          try {
            texture = this.createTextureWithFallback(fullPath);
            if (!texture) {
              // 记录失败的贴图信息
              this.failedTextures.push({
                path: fullPath,
                materialName: name,
                textureType: key,
                error: '贴图创建失败',
                timestamp: new Date()
              });
              continue;
            }

            // 设置非ktx2格式的vScale
            if (!fullPath.toLowerCase().endsWith('.ktx2')) {
              texture.vScale = -1;
            }
            textureDic[key] = texture;
          } catch (error: any) {
            // 记录失败的贴图信息
            this.failedTextures.push({
              path: fullPath,
              materialName: name,
              textureType: key,
              error: error.message || error.toString(),
              timestamp: new Date()
            });
            continue;
          }
        }

        // 将纹理赋值任务加入队列
        texturePromises.push(
          this.handleTextureAssignment(mat, textureDic, key, (texture) => {
            (mat as any)[property] = texture;
          })
        );
      }

      // 更新纹理字典
      degreeTextureDic[name] = textureDic;
    }

    // 等待所有纹理任务完成
    try {
      await Promise.all(texturePromises);

      // 在所有贴图加载完成后设置材质属性
      for (const component of Component) {
        const { name, transparencyMode, bumpTextureLevel } = component;
        if (!name) continue;

        const mat = this.materialDic.Get(name);
        if (!mat) continue;

        mat.transparencyMode = transparencyMode;

        if (mat.bumpTexture) {
          mat.bumpTexture.level = bumpTextureLevel;
        }

        // 应用新的PBR材质属性
        this.applyPBRProperties(mat, component);
      }
    } catch (error) {
      console.error('Error loading textures:', error);
    } finally {
      if (this.mainApp.appDom?.load3D) {
        this.mainApp.appDom.load3D.style.display = "none";
      }
    }

    // 保存更新后的纹理字典
    this.oldTextureDic.Set(degreeId, degreeTextureDic);
  }

  /**
   * 应用PBR材质属性
   * @param mat - PBR材质对象
   * @param component - 配置组件对象
   */
  private applyPBRProperties(mat: PBRMaterial, component: any) {
    // 定义PBR属性映射任务
    const pbrTasks = [
      {
        key: "fresnel",
        value: component.fresnel,
        apply: (value: number) => {
          mat.indexOfRefraction = value;
        }
      },
      {
        key: "clearcoat",
        value: component.clearcoat,
        apply: (value: number) => {
          mat.clearCoat.isEnabled = true;
          mat.clearCoat.intensity = value;
        }
      },
      {
        key: "clearcoatRoughness",
        value: component.clearcoatRoughness,
        apply: (value: number) => {
          mat.clearCoat.roughness = value;
        }
      },
      {
        key: "roughness",
        value: component.roughness,
        apply: (value: number) => {
          mat.roughness = value;
        }
      },
      {
        key: "metallic",
        value: component.metallic,
        apply: (value: number) => {
          mat.metallic = value;
        }
      },
      {
        key: "alpha",
        value: component.alpha,
        apply: (value: number) => {
          mat.alpha = value;
        }
      },
      {
        key: "environmentIntensity",
        value: component.environmentIntensity,
        apply: (value: number) => {
          mat.environmentIntensity = value;
        }
      },
      {
        key: "baseColor",
        value: component.baseColor,
        apply: (value: any) => {
          if (value && typeof value === 'object') {
            const { r, g, b } = value;
            if (r !== null && r !== undefined &&
              g !== null && g !== undefined &&
              b !== null && b !== undefined) {
              mat.albedoColor.set(r, g, b);
            }
          }
        }
      }
    ];

    // 处理每个PBR属性任务
    for (const task of pbrTasks) {
      if (task.value !== null && task.value !== undefined) {
        try {
          task.apply(task.value);
        } catch (error) {
          console.warn('Error applying PBR property:', task.key, error);
        }
      }
    }
  }

  /** 通用的批量卸载贴图资源的方法 */
  private clearTextures(textureDic: Dictionary<any>): Promise<void> {
    return new Promise<void>((resolve) => {
      textureDic.Values().forEach((textures) => {
        for (const key in textures) {
          const texture = textures[key];
          if (texture && texture instanceof Texture) {
            texture.dispose();
          }
        }
      });
      textureDic.Clear();
      resolve();
    });
  }

  /** 处理纹理赋值 */
  private async handleTextureAssignment(mat: any, oldtextureDic: any, textureKey: string, assignCallback: (texture: Texture) => void) {
    const texture = oldtextureDic[textureKey];
    if (texture) {
      await this.checkTextureLoadedWithPromise(texture);
      assignCallback(texture);
    }
  }

  /** 检查纹理是否加载完成 */
  private checkTextureLoadedWithPromise(texture: Texture): Promise<void> {
    return new Promise((resolve) => {
      if (texture.isReady()) {
        resolve();
      } else {
        texture.onLoadObservable.addOnce(() => {
          resolve();
        });
      }
    });
  }

  /** 重置相机位置 */
  reSet() {
    if (this.mainApp.appCamera?.object?.position) {
      this.mainApp.appCamera.object.position.set(160, 50, 0);
    }
  }

  /** 卷帘门开合：再次调用会反向动作 */
  toggleRollerDoor(options?: RollerDoorOptions): void {
    this.setRollerDoorState(!this.rollerDoorIsOpen, options);
  }

  /** 直接设置卷帘门状态 */
  setRollerDoorState(open: boolean, options?: RollerDoorOptions): void {
    const scene = this.mainApp.appScene?.object;
    if (!scene) {
      console.warn('Scene not found for roller door');
      return;
    }

    this.cacheRollerDoorMeshes(options?.meshNames);

    if (!this.rollerDoorGroup || !this.rollerDoorMeshes.length) {
      console.warn('Roller door group or meshes not found');
      return;
    }

    const speed = Math.max(options?.speed ?? 1, 0.01);

    // 计算目标高度
    let targetY: number;
    if (open) {
      // 上升时：如果指定了绝对高度就用绝对高度，否则用相对高度
      if (options?.upY !== undefined) {
        targetY = options.upY;
      } else {
        // 找到所有门中最高的初始位置，让所有门都升到这个高度+3
        const maxBaseY = Math.max(...this.rollerDoorMeshes.map(m =>
          this.rollerDoorInitialY.get(m.name) ?? m.position.y
        ));
        targetY = maxBaseY + 3;
      }
    } else {
      // 下降时：回到初始位置
      targetY = 0;
    }

    // 检查是否已经在目标位置
    if (Math.abs(this.rollerDoorGroup.position.y - targetY) < 0.001) {
      this.rollerDoorIsOpen = open;
      return;
    }

    this.rollerDoorIsOpen = open;
    this.stopRollerDoorAnimation();

    this.rollerDoorObserver = scene.onBeforeRenderObservable.add(() => {
      const dt = scene.getEngine().getDeltaTime() / 1000;
      const current = this.rollerDoorGroup!.position.y;
      const direction = targetY >= current ? 1 : -1;

      // 使用固定速度变量
      const step = speed * dt;
      let next = current + direction * step;

      if ((direction > 0 && next >= targetY) || (direction < 0 && next <= targetY)) {
        next = targetY;
        this.stopRollerDoorAnimation();
        this.rollerDoorIsOpen = open;
        console.log('Roller door animation finished');
      }

      // 移动透明盒子
      this.rollerDoorGroup!.position.y = next;

      // 打印每个卷帘门的当前位置
      console.log('Roller door positions:');
      for (const mesh of this.rollerDoorMeshes) {
        console.log(`${mesh.name}: ${mesh.position.y.toFixed(2)}`);
      }
    });
  }

  /** 当前卷帘门是否开启 */
  isRollerDoorOpen(): boolean {
    return this.rollerDoorIsOpen;
  }

  /**
   * 设置卷帘门的缩放
   * @param meshName - 卷帘门网格名称
   * @param scale - 缩放值（可以是单个数字或 Vector3）
   */
  setRollerDoorScale(meshName: string, scale: number | Vector3): void {
    const mesh = this.meshDic.Get(meshName);
    if (mesh) {
      if (typeof scale === 'number') {
        mesh.scaling.set(scale, scale, scale);
      } else {
        mesh.scaling.copyFrom(scale);
      }
      console.log(`Set scale for ${meshName}:`, mesh.scaling.asArray());
    } else {
      console.warn(`Roller door mesh not found: ${meshName}`);
    }
  }

  /**
   * 设置所有卷帘门的缩放
   * @param scale - 缩放值（可以是单个数字或 Vector3）
   */
  setAllRollerDoorsScale(scale: number | Vector3): void {
    this.rollerDoorMeshes.forEach(mesh => {
      if (typeof scale === 'number') {
        mesh.scaling.set(scale, scale, scale);
      } else {
        mesh.scaling.copyFrom(scale);
      }
      console.log(`Set scale for ${mesh.name}:`, mesh.scaling.asArray());
    });
  }

  /**
   * 设置基于 Y 轴的剖切平面，keepAbove=true 时保留平面以上部分
   * onlyMeshNames 指定只作用于哪些网格，其他网格不受影响
   */
  setYAxisClip(
    height: number,
    keepAbove = true,
    onlyMeshNames?: string[],
    excludeMeshNames?: string[]
  ): void {
    const scene = this.mainApp.appScene?.object;
    if (!scene) {
      console.warn('Scene not found for clipping');
      return;
    }
    const normal = new Vector3(0, keepAbove ? 1 : -1, 0);
    this.yClipPlane = Plane.FromPositionAndNormal(new Vector3(0, height, 0), normal);

    // 如果指定了特定网格，只对这些网格应用剖切
    if (onlyMeshNames?.length) {
      this.applyClipPlaneToMeshes(this.yClipPlane, onlyMeshNames);
    } else {
      // 否则使用场景级别的剖切，作用于所有网格
      scene.clipPlane = this.yClipPlane;
    }

    console.log('[clipping] Scene clipPlane set:', { height, keepAbove, normal: normal.asArray(), targets: onlyMeshNames || 'all' });
  }

  /** 关闭 Y 轴剖切 */
  clearYAxisClip(): void {
    const scene = this.mainApp.appScene?.object;
    if (scene) {
      scene.clipPlane = null;
    }
    this.yClipPlane = null;
    this.yClipTargets = null;

    // 清除所有网格材质上的 clipPlane
    this.meshDic.Values().forEach((mesh) => {
      const mat = mesh.material as any;
      if (mat && 'clipPlane' in mat) {
        mat.clipPlane = null;
      }
    });
  }

  private cacheRollerDoorMeshes(customNames?: string[]): void {
    const scene = this.mainApp.appScene?.object;
    if (!scene) return;

    const names = customNames?.length ? customNames : this.rollerDoorNames;
    this.rollerDoorMeshes = [];

    // 创建或获取 group 作为父级
    if (!this.rollerDoorGroup) {
      // 创建一个 AbstractMesh 作为组
      this.rollerDoorGroup = new AbstractMesh('rollerDoorGroup', scene);
      // 确保 group 的缩放为 1
      // 确保 group 的初始位置为 (0, 0, 0)
      this.rollerDoorGroup.position.set(0, 0, 0);
    }

    for (const name of names) {
      const mesh = this.meshDic.Get(name);
      if (mesh) {
        this.rollerDoorMeshes.push(mesh);

        // 保存网格的当前位置作为初始位置
        if (!this.rollerDoorInitialY.has(name)) {
          this.rollerDoorInitialY.set(name, mesh.position.y);
        }

        // 保存网格的世界位置和缩放
        const worldPosition = mesh.getAbsolutePosition();
        const worldScaling = new Vector3(mesh.scaling.x, mesh.scaling.y, mesh.scaling.z);

        // 将网格添加到 group 中
        mesh.parent = this.rollerDoorGroup;

        // 调整网格的局部位置和缩放，保持世界位置和大小不变
        mesh.setAbsolutePosition(worldPosition);
        mesh.scaling.copyFrom(worldScaling);
      } else {
        console.warn(`Roller door mesh not found: ${name}`);
      }
    }
  }

  private stopRollerDoorAnimation(): void {
    const scene = this.mainApp.appScene?.object;
    if (scene && this.rollerDoorObserver) {
      scene.onBeforeRenderObservable.remove(this.rollerDoorObserver);
    }
    this.rollerDoorObserver = null;
  }

  /** 将 clipPlane 只作用到指定网格的材质 */
  private applyClipPlaneToMeshes(plane: Plane, targetNames: string[]): void {
    const targetSet = new Set(targetNames);
    let appliedCount = 0;

    this.meshDic.Values().forEach((mesh) => {
      const mat = mesh.material as any;
      if (!mat) {
        console.log('[clipping] Mesh has no material:', mesh.name);
        return;
      }

      if (targetSet.has(mesh.name)) {
        // 目标网格：应用剖切
        mat.clipPlane = plane;
        appliedCount++;
        console.log('[clipping] Applied to mesh:', mesh.name, 'material:', mat.name);
      } else {
        // 非目标网格：清除剖切
        mat.clipPlane = null;
      }
    });

    console.log('[clipping] Total meshes processed:', this.meshDic.Keys().length, 'Applied to:', appliedCount);
    if (appliedCount === 0) {
      console.warn('[clipping] No meshes found with names:', targetNames);
      console.log('[clipping] Available mesh names:', this.meshDic.Keys());
    }
  }

  /** 获取公共URL */
  private getPublicUrl(): string {
    // 尝试从环境变量获取
    if (import.meta && import.meta.env && import.meta.env.VITE_PUBLIC_URL) {
      return import.meta.env.VITE_PUBLIC_URL;
    }
    // 默认返回空字符串
    return '';
  }

  /** 清理资源 */
  dispose() {
    this.stopRollerDoorAnimation();
    this.clearYAxisClip();
    this.rollerDoorMeshes = [];
    this.rollerDoorInitialY.clear();
    this.rollerDoorIsOpen = false;

    // 清理 rollerDoorGroup
    if (this.rollerDoorGroup && this.rollerDoorGroup.dispose) {
      this.rollerDoorGroup.dispose();
      this.rollerDoorGroup = null;
    }

    // 清理所有材质资源
    this.materialDic.Values().forEach((material) => {
      if (material && material.dispose) {
        material.dispose();
      }
    });
    this.materialDic.Clear();

    // 清理所有贴图资源
    this.clearTextures(this.oldTextureDic);

    // 清理所有网格
    this.meshDic.Values().forEach((mesh) => {
      if (mesh && mesh.dispose) {
        mesh.dispose();
      }
    });
    this.meshDic.Clear();

    // 清空失败贴图记录
    this.failedTextures = [];
  }

  /** 更新 */
  update() { }

  /** 尝试创建贴图的方法，支持多种格式回退 */
  private createTextureWithFallback(texturePath: string): Texture | null {
    const failureReasons: string[] = [];

    try {
      const texture = new Texture(texturePath);

      if (texture) {
        return texture;
      } else {
        failureReasons.push(`原始路径创建失败: ${texturePath}`);
        throw new Error('Texture creation returned null');
      }
    } catch (error: any) {
      const errorMessage = error.message || error.toString();

      // 特别处理KTX错误
      if (errorMessage.includes('KTX identifier') || errorMessage.includes('missing KTX') ||
        (texturePath.toLowerCase().endsWith('.ktx2') && errorMessage)) {
        this.failedTextures.push({
          path: texturePath,
          textureType: 'KTX2',
          error: `KTX错误: ${errorMessage}`,
          timestamp: new Date()
        });
      }

      failureReasons.push(`原始路径加载异常: ${texturePath} - ${errorMessage}`);

      // 如果是ktx2文件加载失败，尝试查找对应的jpg/png文件
      if (texturePath.toLowerCase().endsWith('.ktx2')) {
        // 尝试jpg格式
        const jpgPath = texturePath.replace(/\.ktx2$/i, '.jpg');
        try {
          const jpgTexture = new Texture(jpgPath);
          if (jpgTexture) {
            return jpgTexture;
          }
        } catch (jpgError: any) {
          failureReasons.push(`JPG回退失败: ${jpgPath} - ${jpgError}`);
        }

        // 尝试png格式
        const pngPath = texturePath.replace(/\.ktx2$/i, '.png');
        try {
          const pngTexture = new Texture(pngPath);
          if (pngTexture) {
            return pngTexture;
          }
        } catch (pngError: any) {
          failureReasons.push(`PNG回退失败: ${pngPath} - ${pngError}`);
        }
      }

      // 所有格式都失败，记录详细失败信息
      this.failedTextures.push({
        path: texturePath,
        textureType: '回退机制',
        error: failureReasons.join('; '),
        timestamp: new Date()
      });

      return null;
    }
  }

  /**
   * 应用材质
   * @param target 目标对象
   * @param material 材质路径
   */
  applyMaterial(target: string, attribute: string, value: number | string): void {
    // 这里需要根据实际的材质管理逻辑实现
    console.log(`Applying attribute ${attribute} to ${value}`);

    // 示例实现：根据目标和材质路径应用材质
    // 1. 查找目标网格
    const targetMaterials: PBRMaterial[] = [];
    this.materialDic.Values().forEach(material => {
      if (material.name.includes(target)) {
        console.log(`${this.materialDic.Get(material.name)}`, material);
        targetMaterials.push(material);
      }
    });

    if (targetMaterials.length === 0) {
      console.warn(`Target not found: ${target}`);
      return;
    }

    // 2. 处理材质路径
    // 这里可以根据材质路径加载对应的材质配置
    // 例如：paint/blue 可以映射到特定的材质配置

    // 3. 应用材质到目标网格
    targetMaterials.forEach(material => {
      if (material[attribute]) {
        material[attribute] = value;
      }
      console.log(`Applying attribute ${attribute} to ${value} to mesh: ${material.name}`);
      // 这里需要根据实际的材质系统实现
    });
  }
}
