<template>
  <el-tooltip
    :content="tooltipContent"
    placement="bottom"
    :show-after="0"
    :hide-after="10"
    transition=""
    :popper-options="{ modifiers: [{ name: 'eventListeners', enabled: false }] }"
    popper-class="non-interactive-tooltip"
  >
    <div class="ws-monitor-toggle" :class="{ 'is-disabled': disabled }" @click="handleToggle">
      <div class="ws-toggle-track" :class="trackClass">
        <div class="ws-status-indicator" :class="statusClass">
          <div class="ws-status-light" :class="lightClass"></div>
        </div>
        <div class="ws-toggle-label">{{ labelText }}</div>
      </div>
    </div>
  </el-tooltip>
</template>

<script setup lang="ts">
import { computed, defineModel, ref, watch } from "vue";
import { useStore } from "vuex";
import { useTimeoutPoll } from "@vueuse/core";
import { get } from "../../../plugs/http/axios3";
import { sendSocketMsg } from "../../../plugs/websocket";
import { useI18n } from "vue3-i18n";

const { t } = useI18n();

// Props interface
interface Props {
  topics: string[]; // Required topics to monitor
  statusEndpoint?: string; // Endpoint to check subscriptions
  interval?: number; // Retry interval (ms)
  maxRetries?: number; // Max connection attempts
  connect?: any; // Subscribe command (object) or function(topics)
  disconnect?: any; // Unsubscribe command (object) or function(topics)
  disabled?: boolean; // Disable toggle
  // Label customization (i18n keys or raw strings)
  labelConnected?: string;
  labelConnecting?: string;
  labelDisconnected?: string;
  labelError?: string;
}

const props = withDefaults(defineProps<Props>(), {
  statusEndpoint: "/public/ws/topics",
  interval: 3000,
  maxRetries: 10,
  disabled: false,
  labelConnected: "ws.autorefresh.enabled",
  labelConnecting: "ws.autorefresh.enabling",
  labelDisconnected: "ws.autorefresh.disabled",
  labelError: "ws.autorefresh.error",
});

// Emits
const emit = defineEmits<{
  "status-change": [connected: boolean];
  error: [error: Error];
}>();

type ConnectionStatus = "disconnected" | "connecting" | "connected" | "error";

// State
const { state } = useStore();
const status = defineModel<ConnectionStatus>({ default: "connecting" });
const currentTopics = ref<string[]>([]);
const retryCount = ref(0);
const lastError = ref<string | null>(null);

// Computed properties
const trackClass = computed(() => ({
  "is-enabled": status.value !== "disconnected",
  "is-disabled": props.disabled,
}));

const statusClass = computed(() => {
  switch (status.value) {
    case "connected":
      return "status-connected";
    case "connecting":
      return "status-connecting";
    case "error":
      return "status-error";
    default:
      return "status-disconnected";
  }
});

const lightClass = computed(() => ({
  "is-pulsing": status.value === "connecting",
}));

const labelText = computed(() => {
  let labelKey: string;
  switch (status.value) {
    case "connected":
      labelKey = props.labelConnected;
      break;
    case "connecting":
      labelKey = props.labelConnecting;
      break;
    case "error":
      labelKey = props.labelError;
      break;
    default:
      labelKey = props.labelDisconnected;
  }
  // Try i18n first, fallback to raw string
  return t(labelKey) || labelKey;
});

const tooltipContent = computed(() => {
  if (status.value === "error" && lastError.value) {
    const errorLabel = t("ws.tooltip.errorOccurred") || "An error occurred";
    return `${errorLabel}: ${lastError.value}`;
  }

  if (status.value === "connecting" && retryCount.value > 0) {
    const retryLabel = t("ws.tooltip.retrying") || "Retrying";
    return `${retryLabel} (${retryCount.value}/${props.maxRetries})`;
  }

  // Default: simple click instruction
  const clickLabel = t("ws.tooltip.clickToToggle") || "Click to enable/disable autorefresh";
  return clickLabel;
});

// Core logic functions
const executeConnect = () => {
  if (!props.connect) return;

  if (typeof props.connect === "function") {
    // If it's a function, call it with topics array and send the returned value
    const result = props.connect(props.topics);
    if (result !== undefined && result !== null) {
      // Handle both single value and array of values
      if (Array.isArray(result)) {
        result.forEach((msg) => sendSocketMsg(msg));
      } else {
        sendSocketMsg(result);
      }
    }
  } else {
    // If it's a value/object, send it directly via WebSocket
    sendSocketMsg(props.connect);
  }
};

const executeDisconnect = () => {
  if (!props.disconnect) return;

  if (typeof props.disconnect === "function") {
    // If it's a function, call it with topics array and send the returned value
    const result = props.disconnect(props.topics);
    if (result !== undefined && result !== null) {
      // Handle both single value and array of values
      if (Array.isArray(result)) {
        result.forEach((msg) => sendSocketMsg(msg));
      } else {
        sendSocketMsg(result);
      }
    }
  } else {
    // If it's a value/object, send it directly via WebSocket
    sendSocketMsg(props.disconnect);
  }
};

const verifyTopics = async (): Promise<boolean> => {
  try {
    const subscribedTopics = await get(props.statusEndpoint, {}, { noLoading: true, noMsg: true });

    if (!Array.isArray(subscribedTopics)) {
      lastError.value = "Failed to fetch topic status";
      emit("error", new Error("Failed to fetch topic status"));
      return false;
    }

    const topicSet = new Set(subscribedTopics);
    currentTopics.value = subscribedTopics;

    // Check if all required topics are present
    return props.topics.every((topic) => topicSet.has(topic));
  } catch (error: any) {
    lastError.value = error.message || "Network error";
    emit("error", error as Error);
    return false;
  }
};

// Verification polling - checks every 1 second if topics are subscribed
const { pause: pauseVerification, resume: resumeVerification } = useTimeoutPoll(
  async () => {
    const allConnected = await verifyTopics();

    if (allConnected) {
      status.value = "connected";
      emit("status-change", true);
      pauseRetry();
      pauseVerification();
      retryCount.value = 0;
      lastError.value = null;
    } else if (!retrying.value) {
      retryCount.value = 0;
      lastError.value = null;
      resumeRetry();
    }
  },
  1000,
  { immediate: false, immediateCallback: true }
);

// Retry polling - retries connection at configurable interval
const {
  isActive: retrying,
  pause: pauseRetry,
  resume: resumeRetry,
} = useTimeoutPoll(
  () => {
    if (retryCount.value >= props.maxRetries) {
      status.value = "error";
      lastError.value = `Max retries (${props.maxRetries}) exceeded`;
      pauseRetry();
      pauseVerification();
      emit("error", new Error(lastError.value));
      return;
    }

    retryCount.value++;
    console.log(`WS Monitor: Retry attempt ${retryCount.value}/${props.maxRetries}`);

    // Execute connect
    executeConnect();
  },
  () => props.interval,
  { immediate: false }
);

const handleToggle = () => {
  if (props.disabled) return;

  if (status.value === "disconnected") {
    status.value = "connecting";
  } else {
    status.value = "disconnected";
  }
};

// Watch for external disable
watch(
  () => props.disabled,
  (newDisabled) => {
    if (newDisabled) {
      status.value = "disconnected"; // Turn off if disabled externally
    }
  }
);

watch(
  status,
  (value, oldValue) => {
    if (value === "connecting") {
      // Turn ON - start connection process
      retryCount.value = 0;
      lastError.value = null;

      // Execute connect
      executeConnect();

      // Start verification and retry polling
      resumeVerification();
      resumeRetry();
    } else if (value === "disconnected" && oldValue !== "disconnected") {
      // Turn OFF - cleanup and disconnect

      // Execute disconnect
      executeDisconnect();

      // Pause all polling
      pauseVerification();
      pauseRetry();

      // Reset state
      retryCount.value = 0;
      lastError.value = null;
      currentTopics.value = [];

      // Emit status change
      emit("status-change", false);
    }
  },
  { immediate: true }
);
</script>

<style scoped lang="scss">
.ws-monitor-toggle {
  display: inline-flex;
  cursor: pointer;
  user-select: none;

  &.is-disabled {
    cursor: not-allowed;
    opacity: 0.5;
  }
}

.ws-toggle-track {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  border-radius: 16px;
  background-color: transparent;
  border: 1px solid v-bind("state.style.borderColor");
  transition: all 0.3s ease;

  &:hover:not(.is-disabled) {
    border-color: v-bind("state.style.primary");
  }

  &.is-enabled {
    background-color: v-bind("state.style.primaryBg");
  }
}

.ws-status-indicator {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
}

.ws-status-light {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  transition: all 0.3s ease;

  &.is-pulsing {
    animation: pulse 1.5s ease-in-out infinite;
  }
}

.status-disconnected .ws-status-light {
  background-color: #909399;
}

.status-connecting .ws-status-light {
  background-color: #e6a23c;
}

.status-connected .ws-status-light {
  background-color: #67c23a;
  box-shadow: 0 0 8px rgba(103, 194, 58, 0.6);
}

.status-error .ws-status-light {
  background-color: #f56c6c;
}

.ws-toggle-label {
  font-size: v-bind("state.size.fontSize + 'px'");
  color: v-bind("state.style.color");
  white-space: nowrap;
}

@keyframes pulse {
  0%,
  100% {
    opacity: 1;
    transform: scale(1);
  }
  50% {
    opacity: 0.5;
    transform: scale(1.2);
  }
}

.non-interactive-tooltip {
  pointer-events: none !important;
}
</style>
