<template>
  <div
    class="compare-cont compare-code-section"
    :class="{ 'is-section-fullscreen': fullscreen, 'is-diff-mode': enableDiff }"
  >
    <div class="compare-code-header">
      <div class="compare-header-main">
        <div class="title">
          <b>{{ $t1(sectionTitle) }}</b>
        </div>
        <div v-if="enableDiff" class="compare-diff-legend">
          <span class="compare-diff-nav">
            <el-button plain
              :type="!diffNavList.length ? 'info' : 'primary'"
              class="button-sty"
              :disabled="!diffNavList.length"
              @click="diffNavList.length && gotoPrevDiff()"
            >
              <i class="el-icon-top"></i>{{ $t1("上一处差异") }}
            </el-button>
            <el-button  plain
              :type="!diffNavList.length ? 'info' : 'primary'"
              class="button-sty"
              :disabled="!diffNavList.length"
              @click="diffNavList.length && gotoNextDiff()"
            >
              <i class="el-icon-bottom"></i>{{ $t1("下一处差异") }}
            </el-button>
            <el-button type="primary" plain @click="gotoTop" class="button-sty">
              <i class="el-icon-caret-top"></i>{{ $t1("返回顶部") }}
            </el-button>
            <span v-if="diffNavList.length" class="diff-nav-count">
              {{ currentDiffIndex < 0 ? "-" : currentDiffIndex + 1 }} /
              {{ diffNavList.length }}
            </span>
          </span>
        </div>
      </div>
      <el-tooltip
        class="item"
        effect="dark"
        :content="$t1('全屏')"
        placement="top"
      >
        <el-link class="is-full" :underline="false" @click="toggleFullscreen">
          <i class="iconfont icon-quanping"></i>
        </el-link>
      </el-tooltip>
    </div>
    <div class="flex compare-code-flex">
      <div class="flex-1 compare-code-col">
        <span class="version-blue">{{ $t1("版本号") }}：{{ preVersion }}</span>
        <div class="compare-code-editor-wrap">
          <code-editor
            ref="leftEditor"
            :mode="mode"
            :readonly="true"
            :value="leftCode"
            :resize="false"
            :height="editorHeight"
          />
        </div>
      </div>
      <div class="flex-1 compare-code-col">
        <span class="version-green">{{ $t1("版本号") }}：{{ curVersion }}</span>
        <div class="compare-code-editor-wrap">
          <code-editor
            ref="rightEditor"
            :mode="mode"
            :readonly="true"
            :value="rightCode"
            :resize="false"
            :height="editorHeight"
          />
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import ace from "ace-builds";
import { diffLines } from "./textDiff";

export default {
  name: "compareCodeSection",
  props: {
    sectionTitle: {
      type: String,
      default: "脚本",
    },
    preVersion: [String, Number],
    curVersion: [String, Number],
    leftCode: {
      type: String,
      default: "",
    },
    rightCode: {
      type: String,
      default: "",
    },
    mode: {
      type: String,
      default: "java",
    },
    enableDiff: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      fullscreen: false,
      diffNavList: [],
      currentDiffIndex: -1,
      diffMarkerIds: {
        left: [],
        right: [],
      },
      activeMarkerIds: {
        left: null,
        right: null,
      },
    };
  },
  computed: {
    editorHeight() {
      return this.fullscreen ? "100%" : "450px";
    },
  },
  watch: {
    leftCode() {
      this.syncAndApplyDiff();
    },
    rightCode() {
      this.syncAndApplyDiff();
    },
    editorHeight() {
      this.resizeEditors();
      if (this.enableDiff) {
        this.syncAndApplyDiff();
      }
    },
    enableDiff(val) {
      if (val) {
        this.syncAndApplyDiff();
      } else {
        this.clearDiffMarkers();
        this.diffNavList = [];
        this.currentDiffIndex = -1;
      }
    },
  },
  mounted() {
    this.syncAndApplyDiff();
  },
  beforeDestroy() {
    this.clearDiffMarkers();
  },
  methods: {
    syncEditorContent() {
      [
        { ref: "leftEditor", code: this.leftCode },
        { ref: "rightEditor", code: this.rightCode },
      ].forEach(({ ref, code }) => {
        const editorComp = this.$refs[ref];
        const aceEditor = editorComp?.aceEditor;
        if (!aceEditor) return;
        const newVal = code || "";
        if (aceEditor.getValue() !== newVal) {
          aceEditor.setValue(newVal, -1);
          aceEditor.clearSelection();
        }
      });
    },
    syncAndApplyDiff() {
      this.$nextTick(() => {
        this.syncEditorContent();
        if (!this.enableDiff) return;
        this.$nextTick(() => {
          this.applyDiffHighlightWithRetry();
        });
      });
    },
    applyDiffHighlightWithRetry(retry = 0) {
      if (!this.enableDiff) return;
      const leftReady = !!this.$refs.leftEditor?.aceEditor;
      const rightReady = !!this.$refs.rightEditor?.aceEditor;
      if ((!leftReady || !rightReady) && retry < 20) {
        setTimeout(() => this.applyDiffHighlightWithRetry(retry + 1), 50);
        return;
      }
      this.syncEditorContent();
      this.applyDiffHighlight();
    },
    toggleFullscreen() {
      this.fullscreen = !this.fullscreen;
      this.$nextTick(() => {
        this.resizeEditors();
        this.syncAndApplyDiff();
      });
    },
    resizeEditors() {
      this.$nextTick(() => {
        ["leftEditor", "rightEditor"].forEach((refName) => {
          const editor = this.$refs[refName];
          if (editor && editor.aceEditor) {
            editor.aceEditor.resize();
          }
        });
      });
    },
    clearDiffMarkers() {
      this.clearActiveMarkers();
      ["leftEditor", "rightEditor"].forEach((refName, index) => {
        const side = index === 0 ? "left" : "right";
        const editor = this.$refs[refName]?.aceEditor;
        if (!editor) return;
        this.diffMarkerIds[side].forEach((id) => {
          editor.session.removeMarker(id);
        });
        this.diffMarkerIds[side] = [];
      });
    },
    clearActiveMarkers() {
      ["leftEditor", "rightEditor"].forEach((refName, index) => {
        const side = index === 0 ? "left" : "right";
        const editor = this.$refs[refName]?.aceEditor;
        const markerId = this.activeMarkerIds[side];
        if (editor && markerId != null) {
          editor.session.removeMarker(markerId);
        }
        this.activeMarkerIds[side] = null;
      });
    },
    applyDiffHighlight() {
      if (!this.enableDiff) return;

      const leftEditor = this.$refs.leftEditor?.aceEditor;
      const rightEditor = this.$refs.rightEditor?.aceEditor;
      if (!leftEditor || !rightEditor) return;

      this.syncEditorContent();
      this.clearDiffMarkers();

      const leftText = leftEditor.getValue();
      const rightText = rightEditor.getValue();
      const { oldMarks, newMarks, diffNavList } = diffLines(
        leftText,
        rightText
      );
      this.diffNavList = diffNavList || [];
      this.currentDiffIndex = -1;
      const Range = ace.require("ace/range").Range;

      this.diffMarkerIds.left = oldMarks.map((row) =>
        leftEditor.session.addMarker(
          new Range(row, 0, row, Number.MAX_VALUE),
          "compare-diff-old",
          "fullLine",
          false
        )
      );
      this.diffMarkerIds.right = newMarks.map((row) =>
        rightEditor.session.addMarker(
          new Range(row, 0, row, Number.MAX_VALUE),
          "compare-diff-new",
          "fullLine",
          false
        )
      );
    },
    gotoPrevDiff() {
      if (!this.diffNavList.length) return;
      this.currentDiffIndex =
        (this.currentDiffIndex - 1 + this.diffNavList.length) %
        this.diffNavList.length;
      this.locateCurrentDiff();
    },
    gotoNextDiff() {
      if (!this.diffNavList.length) return;
      this.currentDiffIndex =
        (this.currentDiffIndex + 1) % this.diffNavList.length;
      this.locateCurrentDiff();
    },
    gotoTop() {
      const leftEditor = this.$refs.leftEditor?.aceEditor;
      const rightEditor = this.$refs.rightEditor?.aceEditor;
      if (!leftEditor || !rightEditor) return;

      this.clearActiveMarkers();
      this.currentDiffIndex = -1;
      this.scrollEditorToLine(leftEditor, 0);
      this.scrollEditorToLine(rightEditor, 0);
    },
    resolveDiffScrollRows(diffItem) {
      const leftRow =
        diffItem.left != null ? diffItem.left : diffItem.anchorLeft ?? 0;
      const rightRow =
        diffItem.right != null ? diffItem.right : diffItem.anchorRight ?? 0;
      return { leftRow, rightRow };
    },
    addActiveHunkMarker(editor, side, diffItem) {
      const Range = ace.require("ace/range").Range;
      const isLeft = side === "left";
      const start = isLeft ? diffItem.left : diffItem.right;
      const end = isLeft ? diffItem.leftEnd : diffItem.rightEnd;

      if (start == null) return null;

      const endRow = end != null ? end : start;
      return editor.session.addMarker(
        new Range(start, 0, endRow, Number.MAX_VALUE),
        isLeft ? "compare-diff-active-old" : "compare-diff-active-new",
        "fullLine",
        false
      );
    },
    scrollEditorToLine(editor, row) {
      const maxRow = Math.max(editor.session.getLength() - 1, 0);
      const targetRow = Math.max(0, Math.min(row, maxRow));
      editor.scrollToLine(targetRow, true, true, () => {});
      editor.gotoLine(targetRow + 1, 0, true);
      editor.renderer.scrollCursorIntoView({ row: targetRow, column: 0 }, 0.5);
    },
    locateCurrentDiff() {
      const diffItem = this.diffNavList[this.currentDiffIndex];
      if (!diffItem) return;

      const leftEditor = this.$refs.leftEditor?.aceEditor;
      const rightEditor = this.$refs.rightEditor?.aceEditor;
      if (!leftEditor || !rightEditor) return;

      this.clearActiveMarkers();
      const { leftRow, rightRow } = this.resolveDiffScrollRows(diffItem);

      this.scrollEditorToLine(leftEditor, leftRow);
      this.scrollEditorToLine(rightEditor, rightRow);

      this.activeMarkerIds.left = this.addActiveHunkMarker(
        leftEditor,
        "left",
        diffItem
      );
      this.activeMarkerIds.right = this.addActiveHunkMarker(
        rightEditor,
        "right",
        diffItem
      );
    },
  },
};
</script>

<style scoped lang="scss">
@import "./compareContMixin.scss";

.compare-code-section {
  @include compare-cont-shell;
  @include compare-cont-full-link;
  @include compare-version-label;
  @include compare-flex-row;
  padding:0 16px 16px;
  overflow: hidden;
  .compare-code-flex {
    margin-top: 0;

    .compare-code-col {
      .version-blue,
      .version-green {
        margin-bottom: 8px;
      }
    }

    ::v-deep .ace-container {
      border: 1px solid #ebeef5;
    }
  }

  .compare-code-header {
    position: relative;
    min-height: 28px;
    padding-right: 24px;
    margin-bottom:16px;
  }

  .compare-header-main {
    position:relative;
  }

  .title {

  }

  .compare-diff-legend {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    font-size: 12px;
    gap: 0 12px;
    position: absolute;
    top: 8px;
    left: 127px;
    .legend-item {
      display: inline-block;
      padding: 2px 8px;
      border-radius: 2px;

      &.legend-old {
        background: rgba(245, 108, 108, 0.2);
        color: #f56c6c;
      }

      &.legend-new {
        background: rgba(103, 194, 58, 0.2);
        color: #67c23a;
      }
    }

    .compare-diff-nav {
      display: inline-flex;
      align-items: center;
      gap: 8px;

      .diff-nav-btn {
        display: inline-flex;
        align-items: center;
        height: 24px;
        padding: 0 10px;
        font-size: 12px;
        line-height: 22px;
        color: #409eff;
        background: #ecf5ff;
        border: 1px solid #b3d8ff;
        border-radius: 2px;
        cursor: pointer;
        white-space: nowrap;
        user-select: none;
        transition: all 0.2s;

        i {
          margin-right: 4px;
          font-size: 12px;
        }

        &:hover:not(.is-disabled) {
          color: #fff;
          background: #409eff;
          border-color: #409eff;
        }

        &.is-disabled {
          color: #c0c4cc;
          background: #f5f7fa;
          border-color: #e4e7ed;
          cursor: not-allowed;
        }
      }

      .diff-nav-count {
        color: #909399;
        white-space: nowrap;
        padding-left: 4px;
      }
    }
  }

  &.is-diff-mode {
    ::v-deep .ace_marker-layer {
      .compare-diff-old {
        position: absolute;
        background: rgba(245, 108, 108, 0.2);
        z-index: 3;
      }

      .compare-diff-new {
        position: absolute;
        background: rgba(103, 194, 58, 0.2);
        z-index: 3;
      }

      .compare-diff-active-old {
        position: absolute;
        background: rgba(245, 108, 108, 0.35);
        z-index: 4;
      }

      .compare-diff-active-new {
        position: absolute;
        background: rgba(103, 194, 58, 0.35);
        z-index: 4;
      }
    }
  }

  &.is-section-fullscreen {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 9999;
    display: flex;
    flex-direction: column;
    margin: 0;
    overflow: hidden;
    background:#FFF;border-radius:12px;

    .compare-code-header {
      flex-shrink: 0;
    }

    .compare-code-flex {
      flex: 1;
      min-height: 0;
      align-items: stretch;
    }

    .compare-code-col {
      display: flex;
      flex-direction: column;
      min-height: 0;
    }

    .compare-code-editor-wrap {
      flex: 1;
      min-height: 0;

      ::v-deep .ace-container {
        height: 100%;
      }

      ::v-deep .ace-editor {
        height: 100% !important;
      }
    }
  }
}
</style>
