<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="Verschieben" 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">Laden…</div>
      <div v-else-if="error" class="dashboard-widget__state dashboard-widget__error">{{ error }}</div>
      <div v-else class="dashboard-widget__body">
        <slot :data="data">
          <template v-if="newsDataSingleItem">
            <article class="dashboard-widget__news-single">
              <a v-if="newsDataSingleItem.link" :href="newsDataSingleItem.link" target="_blank" rel="noopener noreferrer" class="dashboard-widget__news-title">{{ newsDataSingleItem.title || '—' }}</a>
              <span v-else class="dashboard-widget__title-text">{{ newsDataSingleItem.title || '—' }}</span>
              <span v-if="newsDataSingleItem.pubDate" class="dashboard-widget__date">{{ formatNewsDate(newsDataSingleItem.pubDate) }}</span>
              <p v-if="newsDataSingleItem.description" class="dashboard-widget__desc">{{ newsDataSingleItem.description }}</p>
            </article>
          </template>
          <template v-else-if="falukantData">
            <dl class="dashboard-widget__falukant">
              <dt>{{ $t('falukant.overview.metadata.name') }}</dt>
              <dd>{{ falukantDisplayName }}</dd>
              <dt>{{ $t('falukant.create.gender') }}</dt>
              <dd class="falukant-gender">{{ falukantGenderLabel }}</dd>
              <dt>{{ $t('falukant.overview.metadata.age') }}</dt>
              <dd class="falukant-age">{{ falukantAgeLabel }}</dd>
              <dt>{{ $t('falukant.overview.metadata.money') }}</dt>
              <dd>{{ formatMoney(falukantData.money) }}</dd>
              <dt>{{ $t('falukant.messages.title') }}</dt>
              <dd>{{ falukantData.unreadNotificationsCount }}</dd>
              <dt>{{ $t('falukant.statusbar.children') }}</dt>
              <dd>{{ falukantData.childrenCount }}</dd>
            </dl>
          </template>
          <template v-else-if="dataList.length">
            <ul class="dashboard-widget__list">
              <li v-for="(item, i) in dataList" :key="i" class="dashboard-widget__list-item">
                <span v-if="item.datum" class="dashboard-widget__date">{{ formatDatum(item.datum) }}</span>
                <span v-if="item.titel" class="dashboard-widget__title-text">{{ item.titel }}</span>
                <span v-else-if="item.label" class="dashboard-widget__title-text">{{ item.label }}</span>
                <p v-if="item.beschreibung" class="dashboard-widget__desc">{{ item.beschreibung }}</p>
              </li>
            </ul>
          </template>
          <template v-else>{{ defaultContent }}</template>
        </slot>
      </div>
    </div>
  </div>
</template>

<script>
import { mapState } from 'vuex';
import apiClient from '@/utils/axios.js';

export default {
  name: 'DashboardWidget',
  props: {
    widgetId: { type: String, required: true },
    title: { type: String, required: true },
    endpoint: { type: String, required: true },
    /** Wenn true, wird nicht automatisch geladen (z. B. bei Bearbeitung). */
    pauseFetch: { type: Boolean, default: false },
    /** Wievieltes Widget dieser Art (0, 1, 2, …), wird als &counter=N ans EP gehängt (z. B. für News-Pagination). */
    requestCounter: { type: Number, default: undefined }
  },
  emits: ['drag-start', 'drag-end'],
  data() {
    return {
      data: null,
      loading: false,
      error: null,
      isDragging: false,
      _daemonMessageHandler: null
    };
  },
  computed: {
    ...mapState(['socket', 'daemonSocket']),
    isFalukantWidget() {
      return this.endpoint && String(this.endpoint).includes('falukant');
    },
    newsDataResults() {
      const d = this.data;
      if (d && typeof d === 'object' && Array.isArray(d.results)) return d.results;
      return [];
    },
    /** Pro News-Widget wird nur ein Artikel angezeigt (erster der Seite; Counter sorgt für unterschiedliche Seiten). */
    newsDataSingleItem() {
      const list = this.newsDataResults;
      return list.length ? list[0] : null;
    },
    falukantData() {
      const d = this.data;
      if (d && typeof d === 'object' && 'characterName' in d && 'money' in d) return d;
      return null;
    },
    /** Anzeigename: Adelstitel aus falukant.titles.{gender}.{titleLabelTr} + Name. */
    falukantDisplayName() {
      const d = this.falukantData;
      if (!d) return '—';
      const titleKey = d.titleLabelTr;
      const gender = d.gender;
      const nameWithoutTitle = d.nameWithoutTitle ?? d.characterName;
      if (titleKey && gender) {
        const key = `falukant.titles.${gender}.${titleKey}`;
        const translatedTitle = this.$t(key);
        if (translatedTitle !== key) return `${translatedTitle} ${nameWithoutTitle}`.trim();
      }
      return d.characterName || nameWithoutTitle || '—';
    },
    falukantGenderLabel() {
      const g = this.falukantData?.gender;
      if (g == null || g === '') return '—';
      const key = `falukant.create.${g}`;
      const t = this.$t(key);
      return t === key ? this.$t(`general.gender.${g}`) || g : t;
    },
    falukantAgeLabel() {
      const days = this.falukantData?.age;
      if (days == null) return '—';
      const years = Math.floor(Number(days) / 365);
      return `${years} ${this.$t('falukant.overview.metadata.years')}`;
    },
    dataList() {
      if (!Array.isArray(this.data) || this.data.length === 0) return [];
      const first = this.data[0];
      if (first !== null && typeof first === 'object') return this.data;
      return [];
    },
    defaultContent() {
      if (this.data == null) return '';
      if (Array.isArray(this.data)) {
        return this.data.length === 0 ? 'Keine Einträge' : `(${this.data.length} Einträge)`;
      }
      if (typeof this.data === 'object') {
        const keys = Object.keys(this.data);
        return keys.length === 0 ? '—' : `(${keys.length} Felder)`;
      }
      return String(this.data);
    }
  },
  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.isFalukantWidget) this.teardownSocketListeners();
  },
  methods: {
    setupSocketListeners() {
      this.teardownSocketListeners();
      const daemonEvents = ['falukantUpdateStatus', '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.fetchData();
          } catch (_) {}
        };
        this.daemonSocket.addEventListener('message', this._daemonMessageHandler);
      }
      if (this.socket) {
        this.socket.on('falukantUpdateStatus', () => this.fetchData());
        this.socket.on('falukantBranchUpdate', () => this.fetchData());
      }
    },
    teardownSocketListeners() {
      if (this.daemonSocket && this._daemonMessageHandler) {
        this.daemonSocket.removeEventListener('message', this._daemonMessageHandler);
        this._daemonMessageHandler = null;
      }
      if (this.socket) {
        this.socket.off('falukantUpdateStatus');
        this.socket.off('falukantBranchUpdate');
      }
    },
    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?.message || 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);
      this.$emit('drag-start', { widgetId: this.widgetId, event: e });
    },
    onDragEnd() {
      this.isDragging = false;
      this.$emit('drag-end');
    },
    formatDatum(dateStr) {
      if (!dateStr) return '';
      const d = new Date(dateStr);
      if (Number.isNaN(d.getTime())) return String(dateStr);
      return d.toLocaleDateString('de-DE', { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' });
    },
    formatMoney(value) {
      const n = Number(value);
      if (Number.isNaN(n)) return '—';
      return n.toLocaleString('de-DE');
    },
    formatNewsDate(dateStr) {
      if (!dateStr) return '';
      const d = new Date(dateStr);
      if (Number.isNaN(d.getTime())) return String(dateStr);
      return d.toLocaleDateString('de-DE', { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' });
    }
  }
};
</script>

<style scoped>
.dashboard-widget {
  min-height: 200px;
  max-height: 420px;
  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;
}
.dashboard-widget.is-dragging {
  opacity: 0.7;
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}

.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;
}

.dashboard-widget__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.dashboard-widget__list-item {
  padding: 8px 0;
  border-bottom: 1px solid #eee;
}
.dashboard-widget__list-item:last-child {
  border-bottom: none;
}

.dashboard-widget__date {
  display: block;
  font-size: 0.8rem;
  color: #666;
  margin-bottom: 2px;
}

.dashboard-widget__title-text {
  font-weight: 600;
  color: #333;
}

.dashboard-widget__news-single {
  margin: 0;
}

.dashboard-widget__news-title {
  font-weight: 600;
  color: var(--color-primary-orange);
  text-decoration: none;
}
.dashboard-widget__news-title:hover {
  text-decoration: underline;
  color: var(--color-text-secondary);
}

.dashboard-widget__desc {
  margin: 4px 0 0 0;
  font-size: 0.85rem;
  color: #555;
  line-height: 1.4;
}

.dashboard-widget__falukant {
  margin: 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 16px;
  align-items: baseline;
}

.dashboard-widget__falukant dt {
  margin: 0;
  font-weight: 600;
  color: #555;
  font-size: 0.85rem;
}

.dashboard-widget__falukant dd {
  margin: 0;
  font-size: 0.9rem;
}

.dashboard-widget__falukant dd.falukant-gender {
  color: var(--color-text-secondary);
  font-weight: 600;
}

.dashboard-widget__falukant dd.falukant-age {
  color: #198754;
  font-weight: 600;
}
</style>
