<template>
  <div v-show="visible" class="smart-dialog" :style="dialogStyle" @mousedown="onRootMousedown" @dblclick="handleDoubleClick" :class="{ 'no-padding': noPadding, 'is-pending-drag': isPendingDrag, 'is-dragging': isDragging }">
    <div class="dialog-header" :class="{ 'is-draggable': draggable }" @mousedown="startDrag" v-if="title">
      <div class="title-content">
        <slot name="header">
          <span class="title">{{ title }}</span>
        </slot>
      </div>
      <span v-if="showClose" class="close-btn" @click.stop="close">✕</span>
    </div>
    <div v-else class="dialog-header not-title" :class="{ 'is-draggable': draggable }" @mousedown="startDrag"></div>

    <div v-if="title" class="dialog-divider"></div>

    <div class="dialog-body" :class="{ 'no-padding': noPadding }" @mousedown="onBodyMousedown">
      <slot></slot>
    </div>

    <div v-if="resizable" class="resize-handle" @mousedown.prevent="startResize"></div>
  </div>
</template>

<script>

let globalZIndex = 2000;

const DESIGN_WIDTH = 1920; 

export default {
  name: 'SmartDialog',
  props: {
    id: { type: [String, Number], required: true },
    visible: { type: Boolean, default: false },
    title: { type: String, default: '提示' },
    
    center: { type: Boolean, default: true },
    position: { type: Object, default: () => ({ x: 0, y: 0 }) }, 
    
    showClose: { type: Boolean, default: true },
    draggable: { type: Boolean, default: true }, 
    resizable: { type: Boolean, default: true }, 
    noPadding: { type: Boolean, default: false }, 

    defaultWidth: { type: [Number, String], default: 350 },
    defaultHeight: { type: [Number, String], default: 250 },
    minWidth: { type: Number, default: 200 },
    minHeight: { type: Number, default: 150 },
    enableDblclickExpand: { type: Boolean, default: false },
    bringToFrontOnMousedown: { type: Boolean, default: true },
  },
  data() {
    return {
      x: 0,
      y: 0,
      w: 0,
      h: 0,
      currentScale: 1,
      zIndex: globalZIndex,
      isDragging: false,
      isPendingDrag: false,
      isResizing: false,
      dragOffset: { x: 0, y: 0 },
      resizeStart: { x: 0, y: 0, w: 0, h: 0 }
    };
  },
  computed: {
    dialogStyle() {
      let cursor;
      if (this.isDragging || this.isPendingDrag) cursor = 'grabbing';
      else if (this.enableDblclickExpand) cursor = 'pointer';
      else cursor = 'default';
      return {
        left: `${this.x}px`,
        top: `${this.y}px`,
        width: `${this.w}px`,
        height: `${this.h}px`,
        zIndex: this.zIndex,
        cursor,
      };
    }
  },
  created() {
    this.currentScale = this.getRealScale();
    this.w = this._parseSize(this.defaultWidth, window.innerWidth);
    this.h = this._parseSize(this.defaultHeight, window.innerHeight);
    this._lastWinW = window.innerWidth;
    this._lastWinH = window.innerHeight;
  },
  mounted() {
    window.addEventListener('resize', this._onWindowResize);
    if (this.visible) {
      this.bringToFront();
      this.calculatePosition();
    }
  },
  watch: {
    visible(newVal) {
      if (newVal) {
        this.bringToFront();
        this.calculatePosition();
      }
    },
    position: {
      deep: true,
      handler() {
        if (this.visible && !this.center) {
          this.calculatePosition();
        }
      }
    },
    defaultWidth(val) {
      this.w = this._parseSize(val, window.innerWidth);
    },
    defaultHeight(val) {
      this.h = this._parseSize(val, window.innerHeight);
    }
  },
  methods: {
    handleDoubleClick(e) {
      if (!this.enableDblclickExpand) return;
      
      if (e.target.classList.contains('close-btn') || e.target.classList.contains('resize-handle')) {
        return; 
      }

      this.$emit('expand', this.id); 
    },
    getRealScale() {
      return window.innerWidth / DESIGN_WIDTH;
    },

    _parseSize(value, base) {
      if (typeof value === 'string' && value.endsWith('%')) {
        return Math.round((parseFloat(value) / 100) * base);
      }
      const scale = this.getRealScale();
      return Number(value) * scale;
    },
    
    _onWindowResize() {
      setTimeout(() => {
        if (window.innerWidth === this._lastWinW && window.innerHeight === this._lastWinH) {
          return;
        }
        this._lastWinW = window.innerWidth;
        this._lastWinH = window.innerHeight;

        const newScale = this.getRealScale();
        const scaleRatio = newScale / this.currentScale;
        
        this.w = this._parseSize(this.defaultWidth, window.innerWidth);
        this.h = this._parseSize(this.defaultHeight, window.innerHeight);

        if (this.visible) {
          this.x = this.x * scaleRatio;
          this.y = this.y * scaleRatio;

          if (this.x + this.w > window.innerWidth) this.x = window.innerWidth - this.w;
          if (this.y + this.h > window.innerHeight) this.y = window.innerHeight - this.h;
          if (this.x < 0) this.x = 0;
          if (this.y < 0) this.y = 0;
        }
        
        this.currentScale = newScale;
      }, 50); 
    },
    
    close() {
      this.$emit('update:visible', false);
      this.$emit('close');
    },
    
    bringToFront() {
      globalZIndex++;
      this.zIndex = globalZIndex;
    },

    onRootMousedown() {
      if (this.bringToFrontOnMousedown) this.bringToFront();
    },

    onBodyMousedown(e) {
      if (!this.draggable) return;

      const EXEMPT = 'button, input, select, textarea, a, [contenteditable], [tabindex],'
        + ' .drag-handle, .resize-handle, .close-btn, [data-no-drag]';
      if (e.target.closest(EXEMPT)) return;

      const startX = e.clientX, startY = e.clientY;
      const THRESHOLD = 5;
      const HINT_DELAY = 100;
      let dragStarted = false;

      const hintTimer = setTimeout(() => {
        if (!dragStarted) this.isPendingDrag = true;
      }, HINT_DELAY);

      const onMove = (ev) => {
        if (dragStarted) return;
        const dx = Math.abs(ev.clientX - startX);
        const dy = Math.abs(ev.clientY - startY);
        if (dx > THRESHOLD || dy > THRESHOLD) {
          dragStarted = true;
          clearTimeout(hintTimer);
          this.isPendingDrag = false;
          this.startDrag({ clientX: startX, clientY: startY, target: e.target });
        }
      };

      const onUp = () => {
        clearTimeout(hintTimer);
        this.isPendingDrag = false;
        document.removeEventListener('mousemove', onMove);
        document.removeEventListener('mouseup', onUp);
      };

      document.addEventListener('mousemove', onMove);
      document.addEventListener('mouseup', onUp);
    },
    
    calculatePosition() {
      this.$nextTick(() => {
        const winWidth = window.innerWidth;
        const winHeight = window.innerHeight;
        const scale = this.getRealScale();
        
        let targetX = 0;
        let targetY = 0;

        if (this.center) {
          targetX = Math.max(0, (winWidth - this.w) / 2);
          targetY = Math.max(0, (winHeight - this.h) / 2);
        } else {
          targetX = (this.position.x || 0) * scale;
          targetY = (this.position.y || 0) * scale;
        }

        const offsetStep = 20 * scale; 
        let collision = true;
        let attempts = 0; 

        const existingDialogs = document.querySelectorAll('.smart-dialog');

        while (collision && attempts < 15) {
          collision = false;
          for (let i = 0; i < existingDialogs.length; i++) {
            const el = existingDialogs[i];
            if (el === this.$el || el.style.display === 'none') continue; 

            const rect = el.getBoundingClientRect();
            if (Math.abs(rect.left - targetX) < 2 && Math.abs(rect.top - targetY) < 2) {
              collision = true;
              break; 
            }
          }
          if (collision) {
            targetX += offsetStep;
            targetY += offsetStep;
            attempts++;
          }
        }

        if (targetX + this.w > winWidth) targetX = winWidth - this.w - 10;
        if (targetY + this.h > winHeight) targetY = winHeight - this.h - 10;

        this.x = Math.max(0, targetX);
        this.y = Math.max(0, targetY);
      });
    },

    startDrag(e) {
      if (!this.draggable || e.target.classList.contains('close-btn')) return;
      this.isDragging = true;
      this.dragOffset.x = e.clientX - this.x;
      this.dragOffset.y = e.clientY - this.y;
      document.addEventListener('mousemove', this.onDrag);
      document.addEventListener('mouseup', this.stopDrag);
    },
    onDrag(e) {
      if (!this.isDragging) return;
      let newX = e.clientX - this.dragOffset.x;
      let newY = e.clientY - this.dragOffset.y;
      
      const scale = this.getRealScale();
      const safeHeaderHeight = 40 * scale;
      
      this.x = Math.max(0, Math.min(newX, window.innerWidth - this.w));
      this.y = Math.max(0, Math.min(newY, window.innerHeight - safeHeaderHeight)); 
    },
    stopDrag() {
      this.isDragging = false;
      document.removeEventListener('mousemove', this.onDrag);
      document.removeEventListener('mouseup', this.stopDrag);
    },

    startResize(e) {
      if (!this.resizable) return;
      this.isResizing = true;
      this.resizeStart = { x: e.clientX, y: e.clientY, w: this.w, h: this.h };
      document.addEventListener('mousemove', this.onResize);
      document.addEventListener('mouseup', this.stopResize);
    },
    onResize(e) {
      if (!this.isResizing) return;
      const deltaX = e.clientX - this.resizeStart.x;
      const deltaY = e.clientY - this.resizeStart.y;
      
      const scale = this.getRealScale();
      const currentMinWidth = this.minWidth * scale;
      const currentMinHeight = this.minHeight * scale;
      
      this.w = Math.max(currentMinWidth, this.resizeStart.w + deltaX);
      this.h = Math.max(currentMinHeight, this.resizeStart.h + deltaY);
    },
    stopResize() {
      this.isResizing = false;
      document.removeEventListener('mousemove', this.onResize);
      document.removeEventListener('mouseup', this.stopResize);
    }
  },
  beforeDestroy() {
    window.removeEventListener('resize', this._onWindowResize);
    document.removeEventListener('mousemove', this.onDrag);
    document.removeEventListener('mouseup', this.stopDrag);
    document.removeEventListener('mousemove', this.onResize);
    document.removeEventListener('mouseup', this.stopResize);
  }
};
</script>

<style scoped>
.smart-dialog.is-pending-drag,
.smart-dialog.is-pending-drag *,
.smart-dialog.is-dragging,
.smart-dialog.is-dragging * {
  cursor: grabbing !important;
}

.smart-dialog {
  position: fixed;
  background: radial-gradient(circle at 20% 0%, rgba(40,120,200,0.2) 0%, rgba(20,60,130,0.4) 70%);
  box-shadow: inset 0px 0px 0.625rem 0px rgba(88, 146, 255, 0.4), inset 1.25rem 0px 1.875rem -0.625rem rgba(88, 146, 255, 0.15);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 12px;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  user-select: none;
  padding: clamp(2px, 0.6cqw, 6px);
  container-type: inline-size;
}
.smart-dialog.no-padding {
  padding: 0;
}

.dialog-header {
  height: auto;
  background: transparent;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: clamp(10px, 1.2cqw, 12px) clamp(10px, 1.2cqw, 12px);
}

.dialog-header.not-title {
  padding: 0;
}

.dialog-header.is-draggable {
  cursor: move;
}
.not-title.is-draggable {
  height: 10px;
}

.title-content {
  flex: 1;
  min-width: 0;
}

.title {
  color: #ffffff;
  font-size: clamp(14px, 2cqw, 18px);
  font-weight: 500;
  letter-spacing: 1px;
}

.close-btn {
  cursor: pointer;
  color: #ffffff;
  font-size: clamp(11px, 1.8cqw, 16px);
  line-height: 1;
  font-weight: 300;
  opacity: 0.8;
  transition: all 0.2s;
  margin-left:clamp(10px, 2cqw, 15px);
}

.close-btn:hover {
  opacity: 1;
  transform: scale(1.1);
}

.dialog-divider {
  height: 1px;
  background-color: rgba(255, 255, 255, 0.3);
  margin: 0 clamp(2px, 0.6cqw, 6px);
}

.dialog-body {
  flex: 1;
  min-height: 0;
  padding: clamp(6px, 2.2cqw, 20px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.dialog-body.no-padding {
  padding: 0;
}

.dialog-body.no-padding {
  padding: 0;
  color: #e2e8f0;
  overflow: hidden;
  cursor: default;
}

.resize-handle {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 16px;
  height: 16px;
  cursor: se-resize;
  background: linear-gradient(135deg, transparent 50%, rgba(255, 255, 255, 0.2) 50%);
}
</style>