<script setup lang="ts">
import type { AxiosInterceptorManager, AxiosResponse } from 'axios';

import { getCurrentInstance, onMounted, ref, watch } from 'vue';

import { useQuery } from '@tanstack/vue-query';
import { jsonp, request } from '@vtj/utils';
import { createProvider } from '@vtj/web';
import { ElLoading, ElMessage } from 'element-plus';
import { isFn } from 'licia-es';

import { LowCodeService } from './service';

interface WujieProps {
  interceptors?: {
    request: AxiosInterceptorManager<InternalAxiosRequestConfig>;
    response: AxiosInterceptorManager<AxiosResponse>;
  };
  // 必填参数
  fileId: string;
  projectId: number | string;
  // 可选参数
  applicationId?: number | string;
  name?: string;
  [key: string]: any;
}

declare global {
  interface Window {
    $wujie?: {
      bus: {
        $emit: (event: string, data?: any) => void;
      };
      props: WujieProps;
    };
  }
}

// 定义必要的初始化参数
interface InitParams {
  // 必填参数
  fileId: string;
  projectId: number | string;
  // 可选参数
  applicationId?: number | string;
  [key: string]: any;
}

// 响应式状态
const renderer = ref();
const lowCodeService = new LowCodeService();
const isLoading = ref(false);
const provider = ref(null);
const loadingInstance = ref(null);
const errorMessage = ref('');
const initParams = ref<InitParams | null | WujieProps>(null);

// 判断是否为wujie子应用
const isWujieSubApp = window.$wujie !== undefined;

// 从URL解析查询参数
const getParamsFromUrl = (): Partial<InitParams> => {
  const params = new URLSearchParams(window.location.search);
  return {
    fileId: params.get('fileId') || undefined,
    projectId: params.get('projectId') || undefined,
    applicationId: params.get('applicationId') || undefined,
  };
};

// 按优先级获取初始化参数
const getInitParams = (): InitParams | null => {
  if (isWujieSubApp && window.$wujie?.props) {
    const props: WujieProps = window.$wujie.props;
    console.log('WujieProps', props);
    if (props.fileId && props.projectId) {
      console.log('使用无界初始化渲染器');
      return props as WujieProps;
    }
  }

  // 2. 其次从URL参数获取
  const urlParams = getParamsFromUrl();
  if (urlParams.fileId && urlParams.projectId) {
    console.log('使用URL参数初始化渲染器:', urlParams);
    return urlParams as InitParams;
  }

  // 3. 都不满足，返回null
  console.error('无法获取初始化参数');
  errorMessage.value = '无法获取初始化参数，请检查无界配置或URL参数';
  return null;
};

// 检查参数是否有效
const isValidParams = (params: InitParams | null): params is InitParams => {
  return !!params && !!params.fileId && !!params.projectId;
};

// 初始化请求配置
const initRequestConfig = () => {
  if (!isWujieSubApp) return console.log('不是无界渲染模式，不进行拦截器合并');
  const props = window.$wujie.props;
  const mergeRequestInterceptors = () => {
    const requestHandlers = props.interceptors?.request?.handlers || [];
    requestHandlers.forEach((handler) => {
      if (isFn(handler?.fulfilled)) {
        request.useRequest(
          handler.fulfilled,
          handler.rejected,
          handler.options,
        );
      }
    });
  };

  mergeRequestInterceptors();
};

// 显示加载中
const showLoading = (text = '低代码文件加载中...') => {
  if (loadingInstance.value) return;
  loadingInstance.value = ElLoading.service({ text });
};

// 隐藏加载中
const hideLoading = () => {
  if (loadingInstance.value) {
    loadingInstance.value.close();
    loadingInstance.value = null;
  }
};

// 初始化低代码引擎 - 只初始化一次
const initLowCodeEngine = async () => {
  // 如果已经初始化过，直接返回
  if (provider.value) return provider.value;

  // 检查参数是否有效
  if (!isValidParams(initParams.value)) {
    const error = new Error('缺少必要参数：fileId 和 projectId');
    errorMessage.value = error.message;
    throw error;
  }

  // 初始化请求配置
  initRequestConfig();

  try {
    const { provider: lowCodeProvider, onReady } = createProvider({
      nodeEnv: import.meta.env.NODE_ENV,
      service: lowCodeService,
      project: { id: Number(initParams.value.projectId) },
      adapter: {
        request,
        jsonp,
      },
    });

    provider.value = { provider: lowCodeProvider, onReady };
    return { provider: lowCodeProvider, onReady };
  } catch (error) {
    console.error('初始化低代码引擎失败:', error);
    errorMessage.value = '初始化低代码引擎失败';
    ElMessage.error('初始化低代码引擎失败');
    return Promise.reject(error);
  }
};

// 获取渲染组件
const getRenderComponent = async () => {
  if (!isValidParams(initParams.value)) {
    errorMessage.value = '缺少必要参数：fileId 和 projectId';
    return null;
  }

  isLoading.value = true;
  showLoading();

  try {
    // 1. 确保低代码引擎已初始化
    const { provider: lowCodeProvider, onReady } = await initLowCodeEngine();

    // 2. 获取渲染组件
    return new Promise<any>((resolve) => {
      onReady(async () => {
        const instance = getCurrentInstance();
        instance?.appContext.app.use(lowCodeProvider);
        try {
          const renderComponent = await lowCodeProvider.getRenderComponent(
            initParams.value!.fileId,
          );
          console.log('渲染组件获取成功');
          resolve(renderComponent);
        } catch (error) {
          console.error('获取渲染组件失败:', error);
          errorMessage.value = '获取渲染组件失败';
          ElMessage.error('获取渲染组件失败');
          resolve(null);
        }
      });
    });
  } catch (error) {
    console.error('获取渲染组件过程出错:', error);
    return null;
  } finally {
    isLoading.value = false;
    hideLoading();
  }
};

// 使用 useQuery 管理渲染组件
const { data: rendererComponent, isError } = useQuery({
  queryKey: [
    'getRenderer',
    initParams.value?.fileId,
    initParams.value?.projectId,
  ],
  queryFn: getRenderComponent,
  enabled: false, // 初始不自动执行，改为手动控制
  retry: 1, // 失败后重试一次
  staleTime: 1000 * 60 * 5, // 5分钟内不重新获取
});

// 当组件挂载时，将渲染器组件赋值给 renderer
watch(rendererComponent, (newVal) => {
  if (newVal) {
    renderer.value = newVal;
    console.log('渲染器组件已更新');
  }
});

// 向父应用发送状态消息
const notifyParent = (event: string, data?: any) => {
  if (isWujieSubApp && window.$wujie?.bus) {
    window.$wujie.bus.$emit(event, data);
  }
};

// 组件挂载后执行初始化
onMounted(async () => {
  // 通知父应用已准备就绪（如果是wujie子应用）
  if (isWujieSubApp) {
    notifyParent('ready', 'y-code-renderer is ready');
  }

  // 获取初始化参数 - 在挂载时执行一次
  initParams.value = getInitParams();
  const paramsValid = isValidParams(initParams.value);

  if (!paramsValid) {
    errorMessage.value = '缺少必要参数：fileId 和 projectId';
    if (isWujieSubApp) {
      notifyParent('render-fail', errorMessage.value);
    }
    return;
  }

  try {
    await initLowCodeEngine();

    const component = await getRenderComponent();
    if (component) {
      renderer.value = component;
      if (isWujieSubApp) {
        notifyParent('render-success');
      }
    } else {
      if (isWujieSubApp) {
        notifyParent('render-fail', 'Failed to get component');
      }
    }
  } catch (error) {
    console.error('初始化过程出错:', error);
    if (isWujieSubApp) {
      notifyParent('render-fail', error);
    }
  }
});
</script>

<template>
  <div class="renderer-container">
    <component
      :is="renderer"
      v-if="renderer"
      :ctx-props="initParams"
      v-bind="initParams"
    />

    <div v-else-if="!isValidParams(initParams)" class="error-message">
      {{ errorMessage || '缺少必要参数：fileId 和 projectId' }}
    </div>

    <div v-else-if="!isLoading && isError" class="error-message">
      {{ errorMessage || '组件加载失败，请检查参数和网络连接' }}
    </div>

    <div v-else-if="!isLoading" class="error-message">
      {{ errorMessage || '组件加载失败，请检查控制台日志' }}
    </div>
  </div>
</template>

<style scoped>
.renderer-container {
  box-sizing: border-box;
  padding: 20px;
  height: 100%;
  width: 100%;
}

.error-message {
  margin-top: 50px;
  color: red;
  text-align: center;
}
</style>
