<template>
  <div
    class="z-[1] select-none touch-none"
    :class="selected ? 'outline outline-2 outline-accent outline-offset-2' : ''"
    :style="computedStyle"
    @mousedown.stop="$emit('drag-start', { id: widget.id, event: $event })"
    @touchstart.stop="$emit('drag-start', { id: widget.id, event: $event })"
    @click.stop="$emit('select')"
  >
    <svg class="absolute inset-0" :viewBox="viewBox" preserveAspectRatio="xMidYMid meet">
      <g v-if="scaleTicks.length">
        <line
          v-for="tick in scaleTicks"
          :key="tick.key"
          :x1="tick.x1"
          :y1="tick.y1"
          :x2="tick.x2"
          :y2="tick.y2"
          :stroke="tick.major ? scaleMajorColor : scaleMinorColor"
          :stroke-width="tick.major ? scaleMajorWidth : scaleMinorWidth"
          stroke-linecap="round"
        />
        <text
          v-for="label in scaleLabels"
          :key="label.key"
          :x="label.x"
          :y="label.y"
          :fill="scaleMajorColor"
          :font-size="scaleFontSize"
          text-anchor="middle"
          dominant-baseline="middle"
        >
          {{ label.text }}
        </text>
      </g>
      <path :d="trackPath" :stroke="trackColor" :stroke-width="strokeWidth" stroke-linecap="round" fill="none" />
      <path v-if="valuePath" :d="valuePath" :stroke="indicatorColor" :stroke-width="strokeWidth" stroke-linecap="round" fill="none" />
      <circle
        v-if="knob"
        :cx="knob.x"
        :cy="knob.y"
        :r="knob.r"
        :fill="indicatorColor"
        stroke="#0b0f14"
        stroke-opacity="0.35"
        stroke-width="1"
      />
    </svg>
    <div v-if="showValue" class="absolute font-semibold" :style="valueStyle">
      {{ displayLabel }}
    </div>

    <div
      v-if="selected"
      class="absolute -right-1.5 -bottom-1.5 w-3.5 h-3.5 rounded-[4px] bg-accent border-[2px] border-[#1b1308] shadow-[0_4px_12px_rgba(0,0,0,0.35)] cursor-se-resize z-10"
      data-resize-handle
      @mousedown.stop="$emit('resize-start', { id: widget.id, event: $event })"
      @touchstart.stop="$emit('resize-start', { id: widget.id, event: $event })"
    >
      <span class="absolute right-[2px] bottom-[2px] w-[6px] h-[6px] border-r-2 border-b-2 border-[#1b1308a6] rounded-[2px]"></span>
    </div>
  </div>
</template>

<script setup>
import { computed } from 'vue';
import { fontSizes } from '../../../constants';
import { getBaseStyle, getShadowStyle, getBorderStyle, clamp, hexToRgba } from '../shared/utils';

const props = defineProps({
  widget: { type: Object, required: true },
  scale: { type: Number, default: 1 },
  selected: { type: Boolean, default: false }
});

defineEmits(['select', 'drag-start', 'resize-start']);

const arcMin = computed(() => {
  const val = Number(props.widget.arcMin ?? 0);
  return Number.isFinite(val) ? val : 0;
});
const arcMax = computed(() => {
  const val = Number(props.widget.arcMax ?? 100);
  return Number.isFinite(val) ? val : 100;
});
const scaleMin = computed(() => (arcMax.value <= arcMin.value ? 0 : arcMin.value));
const scaleMax = computed(() => (arcMax.value <= arcMin.value ? 100 : arcMax.value));
const arcRange = computed(() => scaleMax.value - scaleMin.value);

const previewValue = computed(() => {
  if (props.widget.textSrc === 2) return scaleMax.value;
  if (props.widget.textSrc !== 0) return scaleMin.value + arcRange.value * 0.68;

  const parsed = Number.parseFloat(props.widget.text ?? '0');
  if (Number.isNaN(parsed)) return scaleMin.value;
  return parsed;
});

const displayValue = computed(() => {
  return clamp(previewValue.value, scaleMin.value, scaleMax.value);
});

const startAngle = 135;
const endAngle = 45;

const widthPx = computed(() => Math.max(10, props.widget.w * props.scale));
const heightPx = computed(() => Math.max(10, props.widget.h * props.scale));

const strokeWidth = computed(() => {
  const base = Math.max(3, (props.widget.radius || 12) * props.scale);
  const max = Math.min(widthPx.value, heightPx.value) / 2 - 2;
  return Math.max(3, Math.min(base, max));
});

const radius = computed(() => {
  const knobMargin = Math.max(2, strokeWidth.value * 0.35);
  return Math.max(4, Math.min(widthPx.value, heightPx.value) / 2 - strokeWidth.value / 2 - knobMargin);
});

const center = computed(() => ({
  x: widthPx.value / 2,
  y: heightPx.value / 2
}));

const viewBox = computed(() => `0 0 ${widthPx.value} ${heightPx.value}`);

const sweep = computed(() => {
  return endAngle >= startAngle ? endAngle - startAngle : 360 - startAngle + endAngle;
});

function polarToCartesian(cx, cy, r, angleDeg) {
  const rad = (Math.PI / 180) * angleDeg;
  return {
    x: cx + r * Math.cos(rad),
    y: cy + r * Math.sin(rad)
  };
}

function describeArc(cx, cy, r, startDeg, endDeg) {
  const start = polarToCartesian(cx, cy, r, startDeg);
  const end = polarToCartesian(cx, cy, r, endDeg);
  const sweepAngle = endDeg >= startDeg ? endDeg - startDeg : 360 - startDeg + endDeg;
  const largeArc = sweepAngle > 180 ? 1 : 0;
  return `M ${start.x} ${start.y} A ${r} ${r} 0 ${largeArc} 1 ${end.x} ${end.y}`;
}

const trackPath = computed(() => {
  return describeArc(center.value.x, center.value.y, radius.value, startAngle, endAngle);
});

const valueAngle = computed(() => {
  const ratio = arcRange.value > 0 ? (displayValue.value - scaleMin.value) / arcRange.value : 0;
  const deg = (startAngle + sweep.value * clamp(ratio, 0, 1)) % 360;
  return deg;
});

const valuePath = computed(() => {
  if (arcRange.value <= 0) return '';
  if (displayValue.value <= scaleMin.value) return '';
  return describeArc(center.value.x, center.value.y, radius.value, startAngle, valueAngle.value);
});

const knob = computed(() => {
  if (arcRange.value <= 0) return null;
  if (displayValue.value <= scaleMin.value) return null;
  const point = polarToCartesian(center.value.x, center.value.y, radius.value, valueAngle.value);
  return {
    x: point.x,
    y: point.y,
    r: Math.max(4, strokeWidth.value * 0.55)
  };
});

const unitSuffix = computed(() => {
  if (props.widget.arcUnit === 1) return '%';
  if (props.widget.arcUnit === 2) return 'C';
  return '';
});

const displayLabel = computed(() => {
  const value = displayValue.value;
  const useDecimal = props.widget.textSrc === 1;
  const formatted = useDecimal ? value.toFixed(1) : Math.round(value).toString();
  return `${formatted}${unitSuffix.value}`;
});
const showValue = computed(() => props.widget.arcShowValue !== false);
const indicatorColor = computed(() => props.widget.textColor || '#5DD39E');
const valueColor = computed(() => props.widget.arcValueColor || indicatorColor.value);
const valueFontSizeIndex = computed(() => {
  const idx = Number(props.widget.arcValueFontSize);
  if (Number.isFinite(idx) && idx >= 0) return idx;
  return Number(props.widget.fontSize ?? 2);
});

const scaleColor = computed(() => props.widget.arcScaleColor || indicatorColor.value);
const trackColor = computed(() => {
  return hexToRgba(
    props.widget.bgColor || '#274060',
    clamp((props.widget.bgOpacity ?? 180) / 255, 0, 1)
  );
});
const scaleMajorColor = computed(() => hexToRgba(scaleColor.value, 0.75));
const scaleMinorColor = computed(() => hexToRgba(scaleColor.value, 0.35));

const scaleOffset = computed(() => {
  const raw = Number(props.widget.arcScaleOffset ?? 0);
  return (Number.isFinite(raw) ? raw : 0) * props.scale;
});

const scaleMajorWidth = computed(() => Math.max(1, Math.round(strokeWidth.value * 0.16)));
const scaleMinorWidth = computed(() => Math.max(1, Math.round(strokeWidth.value * 0.1)));

const scaleFontSize = computed(() => {
  const size = (fontSizes[props.widget.fontSize] || 22) * props.scale * 0.45;
  return Math.max(9, size);
});

const scaleTicks = computed(() => {
  if (arcRange.value <= 0) return [];
  const totalTicks = 21;
  const majorEvery = 5;
  const baseRadius = Math.max(4, radius.value - strokeWidth.value * 0.2 + scaleOffset.value);
  const majorLen = Math.max(3, strokeWidth.value * 0.35 + 1);
  const minorLen = Math.max(2, strokeWidth.value * 0.2 + 1);
  const ticks = [];

  for (let i = 0; i < totalTicks; i += 1) {
    const ratio = totalTicks > 1 ? i / (totalTicks - 1) : 0;
    const angle = (startAngle + sweep.value * ratio) % 360;
    const isMajor = i % majorEvery === 0 || i === totalTicks - 1;
    const len = isMajor ? majorLen : minorLen;
    const p1 = polarToCartesian(center.value.x, center.value.y, baseRadius, angle);
    const p2 = polarToCartesian(center.value.x, center.value.y, baseRadius - len, angle);
    ticks.push({
      key: `tick-${i}`,
      x1: p1.x,
      y1: p1.y,
      x2: p2.x,
      y2: p2.y,
      major: isMajor,
      ratio
    });
  }

  return ticks;
});

const scaleLabels = computed(() => {
  if (!scaleTicks.value.length) return [];
  const baseRadius = Math.max(4, radius.value - strokeWidth.value * 0.2 + scaleOffset.value);
  const majorLen = Math.max(3, strokeWidth.value * 0.35 + 1);
  const labelGap = Math.max(2, strokeWidth.value * 0.25 + 2);
  const labelRadius = Math.max(6, baseRadius - majorLen - labelGap);

  return scaleTicks.value
    .filter(tick => tick.major)
    .map((tick, idx) => {
      const value = scaleMin.value + tick.ratio * arcRange.value;
      const text = Math.round(value).toString();
      const angle = (startAngle + sweep.value * tick.ratio) % 360;
      const pos = polarToCartesian(center.value.x, center.value.y, labelRadius, angle);
      return {
        key: `label-${idx}`,
        x: pos.x,
        y: pos.y,
        text
      };
    });
});

const valueStyle = computed(() => {
  const size = (fontSizes[valueFontSizeIndex.value] || 22) * props.scale;
  return {
    color: valueColor.value,
    fontSize: `${Math.max(10, size)}px`,
    left: '50%',
    top: '50%',
    transform: 'translate(-50%, -50%)'
  };
});

const computedStyle = computed(() => {
  const w = props.widget;
  const s = props.scale;

  const style = getBaseStyle(w, s);
  style.borderRadius = '50%';
  style.overflow = 'hidden';

  Object.assign(style, getBorderStyle(w, s));
  Object.assign(style, getShadowStyle(w, s));

  return style;
});
</script>
