<script lang="ts" setup>
  import { Button, PageHeader, Empty, Spin, Tooltip } from 'ant-design-vue';
  import { GridItem, GridLayout } from 'vue3-grid-layout';
  import { nextTick, onMounted, ref } from 'vue';
  import WidgetWrapper from '../components/WidgetWrapper/WidgetWrapper.vue';
  import BaseWidgetHeader from '../components/WidgetHeader/BaseWidgetHeader.vue';
  import { DropMenu } from '/@/components/Dropdown';
  import DataBindModal from './components/DataBindModal.vue';
  import { useModal } from '/@/components/Modal';
  import {
    decode,
    DEFAULT_MAX_COL,
    DEFAULT_MIN_HEIGHT,
    DEFAULT_MIN_WIDTH,
    DEFAULT_WIDGET_HEIGHT,
    DEFAULT_WIDGET_WIDTH,
    isBataBoardSharePage,
    MoreActionEvent,
    VisualComponentPermission,
  } from '../config/config';
  import {
    addDataComponent,
    deleteDataComponent,
    getDataComponent,
    getShareBoardComponentInfo,
    updateDataBoardLayout,
  } from '/@/api/dataBoard';
  import { useRoute, useRouter } from 'vue-router';
  import { computed, unref } from '@vue/reactivity';
  import {
    ComponentInfoDetail,
    DataComponentRecord,
    DataSource,
    Layout,
  } from '/@/api/dataBoard/model';
  import { frontComponentMap } from '../components/help';
  import { calcScale } from './config/util';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { DataBoardLayoutInfo } from '../types/type';
  import Authority from '/@/components/Authority/src/Authority.vue';
  import { useSocketConnect } from '../hook/useSocketConnect';
  import { buildUUID } from '/@/utils/uuid';
  // import HistoryTrendModal from './components/HistoryTrendModal.vue';
  import trendIcon from '/@/assets/svg/trend.svg';
  import backIcon from '/@/assets/images/back.png';
  import { useCalcGridLayout } from '../hook/useCalcGridLayout';
  import { FrontComponent } from '../const/const';
  import { useScript } from '/@/hooks/web/useScript';
  import { BAI_DU_MAP_GL_LIB, BAI_DU_MAP_TRACK_ANIMATION } from '/@/utils/fnUtils';

  const ROUTE = useRoute();

  const ROUTER = useRouter();

  const { toPromise: injectBaiDuMapLib } = useScript({ src: BAI_DU_MAP_GL_LIB });
  const { toPromise: injectBaiDuMapTrackAniMationLib } = useScript({
    src: BAI_DU_MAP_TRACK_ANIMATION,
  });

  const { createMessage, createConfirm } = useMessage();

  const getBoardId = computed(() => {
    return decode((ROUTE.params as { boardId: string }).boardId);
  });

  const getDataBoardName = computed(() => {
    return decode((ROUTE.params as { boardName: string }).boardName || '');
  });

  const getSharePageParams = computed(() => {
    const { boardId, tenantId } = ROUTE.params as { boardId: string; tenantId: string };
    return { boardId: decode(boardId), tenantId: decode(tenantId) };
  });

  const getIsSharePage = computed(() => {
    return isBataBoardSharePage(ROUTE.path);
  });

  const widgetEl = new Map<string, Fn>();

  const dataBoardList = ref<DataBoardLayoutInfo[]>([]);

  const draggable = ref(!unref(getIsSharePage));
  const resizable = ref(!unref(getIsSharePage));

  const GirdLayoutColNum = DEFAULT_MAX_COL;
  const GridLayoutMargin = 20;

  const handleBack = () => {
    if (unref(getIsSharePage)) return;
    ROUTER.go(-1);
  };

  function updateSize(i: string, _newH: number, _newW: number, newHPx: number, newWPx: number) {
    newWPx = Number(newWPx);
    newHPx = Number(newHPx);

    const data = dataBoardList.value.find((item) => item.i === i)!;
    const length = data.record.dataSource.length || 0;

    const row = Math.floor(Math.pow(length, 0.5));
    const col = Math.floor(length / row);
    let width = Math.floor(100 / col);
    let height = Math.floor(100 / row);

    const WHRatio = newWPx / newHPx;
    const HWRatio = newHPx / newWPx;

    if (WHRatio > 1.6) {
      width = Math.floor(100 / length);
      height = 100;
    }

    if (HWRatio > 1.6) {
      height = Math.floor(100 / length);
      width = 100;
    }

    data.width = newWPx;
    data.height = newHPx;

    data.record.dataSource = data.record.dataSource.map((item) => {
      if (!item.uuid) item.uuid = buildUUID();
      return {
        ...item,
        width,
        height,
        radio: calcScale(newWPx, newHPx, width, height),
      };
    });

    nextTick(() => {
      const updateFn = widgetEl.get(i);
      if (updateFn) updateFn();
    });
  }

  const itemResize = (i: string, newH: number, newW: number, newHPx: number, newWPx: number) => {
    updateSize(i, newH, newW, newHPx, newWPx);
  };

  const itemResized = (i: string, newH: number, newW: number, newHPx: number, newWPx: number) => {
    handleSaveLayoutInfo();
    updateSize(i, newH, newW, newHPx, newWPx);
  };

  const itemContainerResized = (
    i: string,
    newH: number,
    newW: number,
    newHPx: number,
    newWPx: number
  ) => {
    updateSize(i, newH, newW, newHPx, newWPx);
  };

  const itemMoved = (i: string) => {
    handleSaveLayoutInfo();
    updateCharts(i);
  };

  const updateCharts = (i: string) => {
    nextTick(() => {
      const updateFn = widgetEl.get(i);
      if (updateFn) updateFn();
    });
  };

  const setComponentRef = (el: Element, record: DataBoardLayoutInfo) => {
    if (widgetEl.has(record.i)) widgetEl.delete(record.i);
    if (el && (el as unknown as { update: Fn }).update)
      widgetEl.set(record.i, (el as unknown as { update: Fn }).update);
  };

  const [register, { openModal }] = useModal();

  const handleMoreAction = (event: DropMenu, id: string) => {
    if (event.event === MoreActionEvent.DELETE) {
      createConfirm({
        iconType: 'warning',
        content: '是否确认删除?',
        onOk: () => handleDelete(id),
      });
    }
    if (event.event === MoreActionEvent.EDIT) handleUpdate(id);
    if (event.event === MoreActionEvent.COPY) handleCopy(id);
  };

  const handleOpenCreatePanel = () => {
    openModal(true, { isEdit: false });
  };

  const getLayoutInfo = () => {
    return unref(dataBoardList).map((item) => {
      return {
        id: item.i,
        h: item.h,
        w: item.w,
        x: item.x,
        y: item.y,
      } as Layout;
    });
  };

  const handleSaveLayoutInfo = async () => {
    try {
      await updateDataBoardLayout({
        boardId: unref(getBoardId),
        layout: getLayoutInfo(),
      });
    } catch (error) {}
  };

  const { beginSendMessage } = useSocketConnect(dataBoardList);

  const getBasePageComponentData = async () => {
    try {
      return await getDataComponent(unref(getBoardId));
    } catch (error) {}
    return {} as ComponentInfoDetail;
  };

  const getSharePageComponentData = async () => {
    try {
      const params = unref(getSharePageParams);
      return await getShareBoardComponentInfo(params);
    } catch (error) {}
    return {} as ComponentInfoDetail;
  };

  const getDataBoradDetail = async () => {
    try {
      return unref(getIsSharePage)
        ? await getSharePageComponentData()
        : await getBasePageComponentData();
    } catch (error) {}
    return {} as ComponentInfoDetail;
  };

  const loading = ref(false);
  const getDataBoardComponent = async () => {
    try {
      dataBoardList.value = [];
      loading.value = true;
      const data = await getDataBoradDetail();

      if (!data.data.componentData) {
        dataBoardList.value = [];
        return;
      }
      dataBoardList.value = data.data.componentData.map((item) => {
        const index = data.data.componentLayout.findIndex((each) => item.id === each.id);
        let layout;
        if (!~index) {
          layout = {};
        } else {
          layout = data.data.componentLayout[index];
        }
        return {
          i: item.id,
          w: layout.w || DEFAULT_WIDGET_WIDTH,
          h: layout.h || DEFAULT_WIDGET_HEIGHT,
          x: layout.x || 0,
          y: layout.y || 0,
          record: item,
        };
      });
      beginSendMessage();
    } catch (error) {
      throw error;
    } finally {
      loading.value = false;
    }
  };

  const handleUpdateComponent = async (id: string) => {
    try {
      loading.value = true;
      const data = await getDataBoradDetail();
      const updateIndex = data.data.componentData.findIndex((item) => item.id === id);
      const originalIndex = unref(dataBoardList).findIndex((item) => item.i === id);

      const newUpdateData = data.data.componentData[updateIndex];
      const originalData = unref(dataBoardList)[originalIndex];
      dataBoardList.value[originalIndex] = {
        i: id,
        w: originalData.w || DEFAULT_WIDGET_WIDTH,
        h: originalData.h || DEFAULT_WIDGET_HEIGHT,
        x: originalData.x || 0,
        y: originalData.y || 0,
        width: originalData.width,
        height: originalData.height,
        record: newUpdateData,
      };

      updateSize(id, 0, 0, originalData.height || 0, originalData.width || 0);

      beginSendMessage();
    } catch (error) {
    } finally {
      loading.value = false;
    }
  };

  const getComponent = (record: DataComponentRecord) => {
    const frontComponent = record.frontId;
    const component = frontComponentMap.get(frontComponent as FrontComponent);
    return component?.Component;
  };

  const getComponentConfig = (
    record: DataBoardLayoutInfo['record'],
    dataSourceRecord: DataSource | DataSource[]
  ) => {
    const frontComponent = record.frontId;
    const component = frontComponentMap.get(frontComponent as FrontComponent);
    return component?.transformConfig(component.ComponentConfig || {}, record, dataSourceRecord);
  };

  const handleUpdate = async (id: string) => {
    const record = unref(dataBoardList).find((item) => item.i === id);
    openModal(true, { isEdit: true, record });
  };

  const { calcLayoutInfo } = useCalcGridLayout();

  const handleCopy = async (id: string) => {
    const record = unref(dataBoardList).find((item) => item.i === id);
    try {
      const data = await addDataComponent({
        boardId: unref(getBoardId),
        record: {
          dataBoardId: unref(getBoardId),
          frontId: record?.record.frontId,
          name: record?.record.name,
          remark: record?.record.remark,
          dataSource: record?.record.dataSource,
        },
      });
      createMessage.success('复制成功');
      const _id = data.data.id;
      const layoutInfo = getLayoutInfo();

      const newGridLayout = calcLayoutInfo(unref(dataBoardList), {
        width: record?.w || DEFAULT_WIDGET_HEIGHT,
        height: record?.h || DEFAULT_WIDGET_HEIGHT,
      });
      layoutInfo.push({
        id: _id,
        ...newGridLayout,
      } as Layout);

      await updateDataBoardLayout({
        boardId: unref(getBoardId),
        layout: layoutInfo,
      });

      await getDataBoardComponent();
    } catch (error) {}
  };

  const handleDelete = async (id: string) => {
    try {
      const dataBoardId = unref(dataBoardList).find((item) => item.i == id)?.record.dataBoardId;
      if (!dataBoardId) return;
      await deleteDataComponent({ dataBoardId, ids: [id] });
      createMessage.success('删除成功');
      await getDataBoardComponent();
    } catch (error) {}
  };

  const [registerHistoryDataModal, historyDataModalMethod] = useModal();

  const handleOpenHistroyDataModal = (record: DataSource[]) => {
    historyDataModalMethod.openModal(true, record);
  };

  const hasHistoryTrend = (item: DataBoardLayoutInfo) => {
    return frontComponentMap.get(item.record.frontId as FrontComponent)?.hasHistoryTrend;
  };

  onMounted(async () => {
    injectBaiDuMapLib();
    injectBaiDuMapTrackAniMationLib();
    getDataBoardComponent();
  });
</script>

<template>
  <section class="flex flex-col overflow-hidden h-full w-full board-detail">
    <PageHeader>
      <template #title>
        <div class="flex items-center">
          <img
            :src="backIcon"
            v-if="!getIsSharePage"
            class="mr-3 cursor-pointer"
            @click="handleBack"
          />
          <span class="text-lg" color="#333">{{ getDataBoardName }}</span>
        </div>
      </template>
      <template #extra>
        <Authority :value="VisualComponentPermission.CREATE">
          <Button v-if="!getIsSharePage" type="primary" @click="handleOpenCreatePanel">
            创建组件
          </Button>
        </Authority>
      </template>
      <div>
        <span class="mr-3 text-sm" style="color: #666">已创建组件:</span>
        <span style="color: #409eff"> {{ dataBoardList.length }}个</span>
      </div>
    </PageHeader>
    <section class="flex-1">
      <Spin :spinning="loading">
        <GridLayout
          v-model:layout="dataBoardList"
          :col-num="GirdLayoutColNum"
          :row-height="30"
          :margin="[GridLayoutMargin, GridLayoutMargin]"
          :is-draggable="draggable"
          :is-resizable="resizable"
          :vertical-compact="true"
          :use-css-transforms="true"
          style="width: 100%"
        >
          <GridItem
            v-for="item in dataBoardList"
            :key="item.i"
            :static="item.static"
            :x="item.x"
            :y="item.y"
            :w="item.w"
            :h="item.h"
            :i="item.i"
            :min-h="DEFAULT_MIN_HEIGHT"
            :min-w="DEFAULT_MIN_WIDTH"
            :style="{ display: 'flex', flexWrap: 'wrap' }"
            class="grid-item-layout"
            @resized="itemResized"
            @resize="itemResize"
            @moved="itemMoved"
            @container-resized="itemContainerResized"
            drag-ignore-from=".no-drag"
          >
            <WidgetWrapper
              :key="item.i"
              :ref="(el: Element) => setComponentRef(el, item)"
              :record="item.record"
              :data-source="item.record.dataSource"
            >
              <template #header>
                <BaseWidgetHeader
                  :record="item.record.dataSource"
                  :id="item.record.id"
                  :panel-name="item.record.name"
                  @action="handleMoreAction"
                >
                  <template #moreAction>
                    <Tooltip title="趋势">
                      <img
                        :src="trendIcon"
                        v-if="!getIsSharePage && hasHistoryTrend(item)"
                        class="cursor-pointer w-4.5 h-4.5"
                        @click="handleOpenHistroyDataModal(item.record.dataSource)"
                      />
                    </Tooltip>
                  </template>
                </BaseWidgetHeader>
              </template>
              <template #controls="{ record, add, remove, update }">
                <component
                  :is="getComponent(item.record)"
                  :add="add"
                  :remove="remove"
                  :update="update"
                  :radio="record.radio || {}"
                  v-bind="getComponentConfig(item.record, record)"
                  :random="false"
                />
              </template>
            </WidgetWrapper>
          </GridItem>
        </GridLayout>
        <Empty v-if="!dataBoardList.length" />
      </Spin>
    </section>
    <DataBindModal
      :layout="dataBoardList"
      @register="register"
      @update="handleUpdateComponent"
      @create="getDataBoardComponent"
    />
    <!-- <HistoryTrendModal @register="registerHistoryDataModal" /> -->
  </section>
</template>

<style lang="less" scoped>
  .vue-grid-item:not(.vue-grid-placeholder) {
    background: #fff;
    border: none !important;

    /* border: 1px solid black; */
  }

  .vue-grid-item .resizing {
    opacity: 0.9;
  }

  .vue-grid-item .static {
    background: #cce;
  }

  .vue-grid-item .text {
    font-size: 24px;
    text-align: center;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
    height: 100%;
    width: 100%;
  }

  .vue-grid-item .no-drag {
    height: 100%;
    width: 100%;
  }

  .vue-grid-item .minMax {
    font-size: 12px;
  }

  .vue-grid-item .add {
    cursor: pointer;
  }

  .vue-draggable-handle {
    position: absolute;
    width: 20px;
    height: 20px;
    top: 0;
    left: 0;
    background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><circle cx='5' cy='5' r='5' fill='#999999'/></svg>")
      no-repeat;
    background-position: bottom right;
    padding: 0 8px 8px 0;
    background-repeat: no-repeat;
    background-origin: content-box;
    box-sizing: border-box;
    cursor: pointer;
  }

  .grid-item-layout {
    overflow: hidden;
    border: 1px solid #eee !important;
    background-color: #fcfcfc !important;
  }

  .board-detail:deep(.ant-page-header) {
    padding: 20px 20px 0 20px;
  }

  .board-detail:deep(.ant-page-header-heading) {
    height: 78px;
    padding: 0 20px 0 20px;
    box-sizing: border-box;
    background-color: #fff;
  }

  [data-theme='dark'] .board-detail:deep(.ant-page-header-heading) {
    @apply bg-dark-900;
  }

  .board-detail:deep(.ant-page-header-heading-extra) {
    margin: 0;
    line-height: 78px;
  }

  .board-detail:deep(.ant-page-header-content) {
    padding-top: 20px;
  }

  :deep(.vue-resizable-handle) {
    z-index: 99;
  }
</style>
