<template>
  <div
    class="dashboard-widget"
    :class="{ 'is-dragging': isDragging }"
    :data-widget-id="widgetId"
  >
    <header class="dashboard-widget__titlebar">
      <span class="dashboard-widget__drag-handle" :title="$t('widgets.dashboard.dragHandle')" draggable="true" @dragstart="onDragStart" @dragend="onDragEnd">⋮⋮</span>
      <span class="dashboard-widget__title">{{ title }}</span>
      <slot name="title-actions"></slot>
    </header>
    <div class="dashboard-widget__frame">
      <div v-if="loading" class="dashboard-widget__state">{{ $t('widgets.dashboard.loading') }}</div>
      <div v-else-if="error" class="dashboard-widget__state dashboard-widget__error">{{ error }}</div>
      <div v-else class="dashboard-widget__body">
        <component :is="widgetComponent" :data="data" />
      </div>
    </div>
  </div>
</template>

<script>
import { mapState } from 'vuex';
import apiClient from '@/utils/axios.js';
import FalukantWidget from './widgets/FalukantWidget.vue';
import NewsWidget from './widgets/NewsWidget.vue';
import ListWidget from './widgets/ListWidget.vue';
import BirthdayWidget from './widgets/BirthdayWidget.vue';
import UpcomingEventsWidget from './widgets/UpcomingEventsWidget.vue';
import MiniCalendarWidget from './widgets/MiniCalendarWidget.vue';
import VocabCoursesWidget from './widgets/VocabCoursesWidget.vue';

function getWidgetComponent(endpoint) {
  if (!endpoint || typeof endpoint !== 'string') return ListWidget;
  const ep = endpoint.toLowerCase();
  if (ep.includes('vocab/dashboard-widget')) return VocabCoursesWidget;
  if (ep.includes('falukant')) return FalukantWidget;
  if (ep.includes('news')) return NewsWidget;
  if (ep.includes('calendar/widget/birthdays')) return BirthdayWidget;
  if (ep.includes('calendar/widget/upcoming')) return UpcomingEventsWidget;
  if (ep.includes('calendar/widget/mini')) return MiniCalendarWidget;
  return ListWidget;
}

export default {
  name: 'DashboardWidget',
  components: { FalukantWidget, NewsWidget, ListWidget, BirthdayWidget, UpcomingEventsWidget, MiniCalendarWidget, VocabCoursesWidget },
  props: {
    widgetId: { type: String, required: true },
    title: { type: String, required: true },
    endpoint: { type: String, required: true },
    pauseFetch: { type: Boolean, default: false },
    requestCounter: { type: Number, default: undefined }
  },
  emits: ['drag-start', 'drag-end'],
  data() {
    return {
      data: null,
      loading: false,
      error: null,
      isDragging: false,
      _daemonMessageHandler: null,
      pendingFetchTimer: null
    };
  },
  computed: {
    ...mapState(['socket', 'daemonSocket', 'user']),
    isFalukantWidget() {
      return this.endpoint && String(this.endpoint).includes('falukant');
    },
    widgetComponent() {
      return getWidgetComponent(this.endpoint);
    }
  },
  watch: {
    endpoint: { handler: 'fetchData', immediate: false },
    requestCounter: { handler: 'fetchData', immediate: false },
    pauseFetch: { handler(now) { if (!now) this.fetchData(); } },
    socket(newVal, oldVal) {
      if (!this.isFalukantWidget) return;
      if (oldVal) this.teardownSocketListeners();
      if (newVal) this.setupSocketListeners();
    },
    daemonSocket(newVal, oldVal) {
      if (!this.isFalukantWidget) return;
      if (oldVal) this.teardownSocketListeners();
      if (newVal) this.setupSocketListeners();
    }
  },
  mounted() {
    if (!this.pauseFetch && this.endpoint) this.fetchData();
    if (this.isFalukantWidget) this.setupSocketListeners();
  },
  beforeUnmount() {
    if (this.pendingFetchTimer) {
      clearTimeout(this.pendingFetchTimer);
      this.pendingFetchTimer = null;
    }
    if (this.isFalukantWidget) this.teardownSocketListeners();
  },
  methods: {
    matchesCurrentUser(eventData) {
      if (eventData?.user_id == null) {
        return true;
      }
      const currentIds = [this.user?.id, this.user?.hashedId]
        .filter(Boolean)
        .map((value) => String(value));
      return currentIds.includes(String(eventData.user_id));
    },
    setupSocketListeners() {
      this.teardownSocketListeners();
      const daemonEvents = ['falukantUpdateStatus', 'falukantUpdateFamily', 'falukantUpdateChurch', 'falukantUpdateDebt', 'children_update', 'falukantUpdateProductionCertificate', 'stock_change', 'familychanged'];
      if (this.daemonSocket) {
        this._daemonMessageHandler = (event) => {
          if (event.data === 'ping') return;
          try {
            const data = JSON.parse(event.data);
            if (daemonEvents.includes(data.event) && this.matchesCurrentUser(data)) this.queueFetchData();
          } catch (_) {}
        };
        this.daemonSocket.addEventListener('message', this._daemonMessageHandler);
      }
      if (this.socket) {
        this._statusSocketHandler = (data) => {
          if (this.matchesCurrentUser(data)) this.queueFetchData();
        };
        this._familySocketHandler = (data) => {
          if (this.matchesCurrentUser(data)) this.queueFetchData();
        };
        this._churchSocketHandler = (data) => {
          if (this.matchesCurrentUser(data)) this.queueFetchData();
        };
        this._debtSocketHandler = (data) => {
          if (this.matchesCurrentUser(data)) this.queueFetchData();
        };
        this._childrenSocketHandler = (data) => {
          if (this.matchesCurrentUser(data)) this.queueFetchData();
        };
        this._productionCertificateSocketHandler = (data) => {
          if (this.matchesCurrentUser(data)) this.queueFetchData();
        };
        this._branchSocketHandler = () => this.queueFetchData();

        this.socket.on('falukantUpdateStatus', this._statusSocketHandler);
        this.socket.on('falukantUpdateFamily', this._familySocketHandler);
        this.socket.on('falukantUpdateChurch', this._churchSocketHandler);
        this.socket.on('falukantUpdateDebt', this._debtSocketHandler);
        this.socket.on('children_update', this._childrenSocketHandler);
        this.socket.on('falukantUpdateProductionCertificate', this._productionCertificateSocketHandler);
        this.socket.on('falukantBranchUpdate', this._branchSocketHandler);
      }
    },
    teardownSocketListeners() {
      if (this.daemonSocket && this._daemonMessageHandler) {
        this.daemonSocket.removeEventListener('message', this._daemonMessageHandler);
        this._daemonMessageHandler = null;
      }
      if (this.socket) {
        if (this._statusSocketHandler) this.socket.off('falukantUpdateStatus', this._statusSocketHandler);
        if (this._familySocketHandler) this.socket.off('falukantUpdateFamily', this._familySocketHandler);
        if (this._churchSocketHandler) this.socket.off('falukantUpdateChurch', this._churchSocketHandler);
        if (this._debtSocketHandler) this.socket.off('falukantUpdateDebt', this._debtSocketHandler);
        if (this._childrenSocketHandler) this.socket.off('children_update', this._childrenSocketHandler);
        if (this._productionCertificateSocketHandler) this.socket.off('falukantUpdateProductionCertificate', this._productionCertificateSocketHandler);
        if (this._branchSocketHandler) this.socket.off('falukantBranchUpdate', this._branchSocketHandler);
      }
    },
    queueFetchData() {
      if (this.pendingFetchTimer) {
        clearTimeout(this.pendingFetchTimer);
      }
      this.pendingFetchTimer = setTimeout(() => {
        this.pendingFetchTimer = null;
        this.fetchData();
      }, 120);
    },
    async fetchData() {
      if (!this.endpoint || this.pauseFetch) return;
      this.loading = true;
      this.error = null;
      try {
        let path = this.endpoint.startsWith('/') ? this.endpoint : `/${this.endpoint}`;
        path = path.startsWith('/api') ? path : `/api${path}`;
        if (this.requestCounter !== undefined && this.requestCounter !== null) {
          const sep = path.includes('?') ? '&' : '?';
          path = `${path}${sep}counter=${Number(this.requestCounter)}`;
        }
        const res = await apiClient.get(path);
        this.data = res.data;
      } catch (e) {
        this.error = e.response?.data?.error || e.message || 'Fehler beim Laden';
      } finally {
        this.loading = false;
      }
    },
    onDragStart(e) {
      this.isDragging = true;
      e.dataTransfer.effectAllowed = 'move';
      e.dataTransfer.setData('text/plain', this.widgetId);
      e.dataTransfer.setData('application/x-widget-id', this.widgetId);
      const dragGhost = this.$el.cloneNode(true);
      dragGhost.style.position = 'absolute';
      dragGhost.style.top = '-9999px';
      dragGhost.style.width = `${this.$el.offsetWidth}px`;
      dragGhost.style.opacity = '0.8';
      dragGhost.style.transform = 'rotate(-2deg)';
      dragGhost.style.pointerEvents = 'none';
      document.body.appendChild(dragGhost);
      const rect = this.$el.getBoundingClientRect();
      const offsetX = e.clientX - rect.left;
      const offsetY = e.clientY - rect.top;
      e.dataTransfer.setDragImage(dragGhost, offsetX, offsetY);
      setTimeout(() => {
        if (dragGhost.parentNode) dragGhost.parentNode.removeChild(dragGhost);
      }, 0);
      this.$emit('drag-start', { widgetId: this.widgetId, event: e });
    },
    onDragEnd() {
      this.isDragging = false;
      this.$emit('drag-end');
    }
  }
};
</script>

<style scoped>
.dashboard-widget {
  min-height: 0;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--dashboard-widget-bg, #fff);
  border: 1px solid var(--dashboard-widget-border, #dee2e6);
  border-radius: 8px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.06);
  overflow: hidden;
  transition: opacity 0.2s ease, box-shadow 0.2s ease;
}

.dashboard-widget.is-dragging {
  opacity: 0.4;
  box-shadow: 0 4px 12px rgba(0,0,0,0.2);
  cursor: grabbing;
  border: 2px dashed #0d6efd;
}

.dashboard-widget__titlebar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  background: var(--color-primary-orange-light, #fff8e1);
  border-bottom: 1px solid var(--color-text-secondary, #dee2e6);
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--color-text-primary);
}

.dashboard-widget__drag-handle {
  cursor: grab;
  color: #868e96;
  user-select: none;
  font-size: 1rem;
  line-height: 1;
}

.dashboard-widget__drag-handle:active {
  cursor: grabbing;
}

.dashboard-widget__title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--color-text-primary);
}

.dashboard-widget__frame {
  flex: 1;
  min-height: 0;
  padding: 12px;
  overflow-y: auto;
}

.dashboard-widget__state {
  color: #666;
  text-align: center;
  padding: 1rem;
}

.dashboard-widget__error {
  color: #c92a2a;
}

.dashboard-widget__body {
  font-size: 0.9rem;
  color: #333;
}
</style>
