<template>
  <BaseDialog
    :model-value="modelValue"
    title="Gruppenfoto verarbeiten"
    size="large"
    :width="1180"
    max-width="96vw"
    @update:model-value="emitClose"
    @close="emitClose"
  >
    <div class="group-photo-dialog">
      <section class="group-photo-start">
        <div class="group-photo-actions">
          <input ref="fileInput" type="file" accept="image/*" class="hidden-input" @change="handleFileChange">
          <input ref="cameraInput" type="file" accept="image/*" capture="environment" class="hidden-input" @change="handleFileChange">
          <button type="button" @click="$refs.cameraInput?.click()">Foto aufnehmen</button>
          <button type="button" @click="$refs.fileInput?.click()">Foto hochladen</button>
          <button type="button" @click="loadSavedPhotos" :disabled="loadingSaved">
            {{ loadingSaved ? 'Lade ...' : 'Gespeicherte Fotos laden' }}
          </button>
        </div>
        <p class="group-photo-hint">
          Das Gruppenfoto kann nur lokal verarbeitet oder für spätere Bearbeitung im Verein gespeichert werden.
        </p>
      </section>

      <section v-if="savedPhotos.length" class="saved-photo-list">
        <h3>Gespeicherte Gruppenfotos</h3>
        <div class="saved-photo-grid">
          <article v-for="photo in savedPhotos" :key="photo.id" class="saved-photo-item">
            <div>
              <strong>{{ photo.title }}</strong>
              <span>{{ formatDate(photo.takenAt || photo.createdAt) }}</span>
            </div>
            <div class="saved-photo-actions">
              <button type="button" @click="openSavedPhoto(photo)">Öffnen</button>
              <button type="button" class="danger-button" @click="deleteSavedPhoto(photo)">Löschen</button>
            </div>
          </article>
        </div>
      </section>

      <section v-if="sourceUrl" class="save-source-panel">
        <label class="checkbox-line">
          <input type="checkbox" v-model="saveForLater">
          Für spätere Bearbeitung speichern
        </label>
        <div v-if="saveForLater" class="save-source-fields">
          <label>
            <span>Titel</span>
            <input v-model="sourceTitle" type="text" placeholder="Training 15.04.2026">
          </label>
          <label>
            <span>Datum</span>
            <input v-model="sourceTakenAt" type="date">
          </label>
          <label>
            <span>Notiz</span>
            <input v-model="sourceDescription" type="text" placeholder="optional">
          </label>
          <button type="button" class="btn-primary" :disabled="savingSource || !sourceFile || currentSavedPhoto" @click="saveSourcePhoto">
            {{ currentSavedPhoto ? 'Gruppenfoto gespeichert' : savingSource ? 'Speichere ...' : 'Gruppenfoto speichern' }}
          </button>
        </div>
        <p v-if="saveForLater" class="privacy-note">
          Dieses Gruppenfoto wird gespeichert und kann andere Personen enthalten. Bitte nur speichern, wenn die Nutzung im Verein zulässig ist.
        </p>
      </section>

      <section v-if="sourceUrl" class="crop-workspace">
        <div class="crop-stage">
          <div
            ref="imageFrame"
            class="crop-image-frame"
            @mousedown="startSelection"
            @mousemove="moveSelection"
            @mouseup="finishSelection"
            @mouseleave="finishSelection"
            @touchstart.prevent="startSelection"
            @touchmove.prevent="moveSelection"
            @touchend.prevent="finishSelection"
          >
            <img
              ref="sourceImage"
              :src="sourceUrl"
              alt="Gruppenfoto"
              draggable="false"
              @load="handleImageLoad"
            >
            <div
              v-if="selection"
              class="crop-selection"
              :style="selectionStyle"
            ></div>
          </div>
          <p class="group-photo-hint">Rahmen um eine Person ziehen. Der Ausschnitt wird quadratisch als Mitgliedsfoto gespeichert.</p>
        </div>

        <aside class="crop-side">
          <div>
            <h3>Ausschnitt</h3>
            <div class="crop-preview">
              <img v-if="previewUrl" :src="previewUrl" alt="Ausschnitt Vorschau">
              <span v-else>Kein Ausschnitt</span>
            </div>
            <p v-if="smallCropWarning" class="warning-text">Der Ausschnitt ist sehr klein und kann unscharf wirken.</p>
          </div>

          <label class="member-search">
            <span>Mitglied suchen</span>
            <input v-model="memberQuery" type="search" placeholder="Name eingeben">
          </label>
          <select v-model="selectedMemberId" class="member-select">
            <option value="">Mitglied auswählen</option>
            <option v-for="member in filteredMembers" :key="member.id" :value="member.id">
              {{ member.lastName }}, {{ member.firstName }}
            </option>
          </select>

          <label class="checkbox-line">
            <input type="checkbox" v-model="makePrimary">
            Als Hauptfoto verwenden
          </label>

          <button type="button" class="btn-primary" :disabled="!canSaveCrop || savingCrop" @click="saveCrop">
            {{ savingCrop ? 'Speichere ...' : 'Als Mitgliedsfoto speichern' }}
          </button>
          <button type="button" @click="resetSelection">Auswahl zurücksetzen</button>
          <p v-if="statusMessage" class="status-message">{{ statusMessage }}</p>
          <p v-if="errorMessage" class="error-message">{{ errorMessage }}</p>
        </aside>
      </section>
    </div>
  </BaseDialog>
</template>

<script>
import BaseDialog from '../BaseDialog.vue';
import apiClient from '../../apiClient.js';

export default {
  name: 'GroupPhotoCropDialog',
  components: { BaseDialog },
  props: {
    modelValue: { type: Boolean, required: true },
    clubId: { type: [String, Number], required: true },
    members: { type: Array, required: true }
  },
  emits: ['update:modelValue', 'member-image-updated'],
  data() {
    return {
      sourceUrl: '',
      sourceFile: null,
      sourceTitle: '',
      sourceTakenAt: '',
      sourceDescription: '',
      saveForLater: false,
      currentSavedPhoto: null,
      savedPhotos: [],
      loadingSaved: false,
      savingSource: false,
      naturalSize: { width: 0, height: 0 },
      selection: null,
      selecting: false,
      selectionStart: null,
      previewUrl: '',
      previewBlob: null,
      memberQuery: '',
      selectedMemberId: '',
      makePrimary: true,
      savingCrop: false,
      statusMessage: '',
      errorMessage: ''
    };
  },
  computed: {
    filteredMembers() {
      const query = this.memberQuery.trim().toLowerCase();
      return [...this.members]
        .filter(member => {
          if (!query) return true;
          return [member.firstName, member.lastName].filter(Boolean).join(' ').toLowerCase().includes(query);
        })
        .sort((a, b) => String(a.lastName || '').localeCompare(String(b.lastName || ''), 'de-DE'));
    },
    selectionStyle() {
      if (!this.selection) return {};
      const frameRect = this.$refs.imageFrame?.getBoundingClientRect();
      const imageRect = this.$refs.sourceImage?.getBoundingClientRect();
      const offsetX = frameRect && imageRect ? imageRect.left - frameRect.left : 0;
      const offsetY = frameRect && imageRect ? imageRect.top - frameRect.top : 0;
      return {
        left: `${this.selection.x + offsetX}px`,
        top: `${this.selection.y + offsetY}px`,
        width: `${this.selection.size}px`,
        height: `${this.selection.size}px`
      };
    },
    canSaveCrop() {
      return !!this.previewBlob && !!this.selectedMemberId;
    },
    smallCropWarning() {
      return this.selection && this.selection.size > 0 && this.selection.size < 80;
    }
  },
  watch: {
    modelValue(isOpen) {
      if (isOpen && !this.savedPhotos.length) {
        this.loadSavedPhotos();
      }
    }
  },
  beforeUnmount() {
    this.revokeSourceUrl();
    this.revokePreviewUrl();
  },
  methods: {
    emitClose() {
      this.$emit('update:modelValue', false);
    },
    async handleFileChange(event) {
      const file = event.target.files?.[0];
      event.target.value = '';
      if (!file) return;
      if (!file.type.startsWith('image/')) {
        this.errorMessage = 'Bitte eine Bilddatei auswählen.';
        return;
      }
      this.revokeSourceUrl();
      this.revokePreviewUrl();
      this.sourceFile = file;
      this.sourceUrl = URL.createObjectURL(file);
      this.sourceTitle = `Training ${new Date().toLocaleDateString('de-DE')}`;
      this.sourceTakenAt = new Date().toISOString().slice(0, 10);
      this.sourceDescription = '';
      this.currentSavedPhoto = null;
      this.selection = null;
      this.previewBlob = null;
      this.statusMessage = '';
      this.errorMessage = '';
    },
    handleImageLoad() {
      const image = this.$refs.sourceImage;
      this.naturalSize = {
        width: image?.naturalWidth || 0,
        height: image?.naturalHeight || 0
      };
    },
    getPointer(event) {
      const point = event.touches?.[0] || event.changedTouches?.[0] || event;
      const rect = this.$refs.imageFrame.getBoundingClientRect();
      const image = this.$refs.sourceImage;
      const imageRect = image.getBoundingClientRect();
      return {
        x: Math.max(0, Math.min(point.clientX - imageRect.left, imageRect.width)),
        y: Math.max(0, Math.min(point.clientY - imageRect.top, imageRect.height)),
        maxWidth: imageRect.width,
        maxHeight: imageRect.height
      };
    },
    startSelection(event) {
      if (!this.sourceUrl) return;
      const pointer = this.getPointer(event);
      this.selecting = true;
      this.selectionStart = pointer;
      this.selection = { x: pointer.x, y: pointer.y, size: 0 };
      this.revokePreviewUrl();
    },
    moveSelection(event) {
      if (!this.selecting || !this.selectionStart) return;
      const pointer = this.getPointer(event);
      const dx = pointer.x - this.selectionStart.x;
      const dy = pointer.y - this.selectionStart.y;
      const size = Math.min(Math.abs(dx), Math.abs(dy));
      const x = dx < 0 ? this.selectionStart.x - size : this.selectionStart.x;
      const y = dy < 0 ? this.selectionStart.y - size : this.selectionStart.y;
      this.selection = {
        x: Math.max(0, Math.min(x, pointer.maxWidth - size)),
        y: Math.max(0, Math.min(y, pointer.maxHeight - size)),
        size
      };
    },
    async finishSelection() {
      if (!this.selecting) return;
      this.selecting = false;
      if (!this.selection || this.selection.size < 20) {
        this.resetSelection();
        return;
      }
      await this.updatePreview();
    },
    async updatePreview() {
      const blob = await this.createCropBlob();
      if (!blob) return;
      this.revokePreviewUrl();
      this.previewBlob = blob;
      this.previewUrl = URL.createObjectURL(blob);
    },
    async createCropBlob() {
      const image = this.$refs.sourceImage;
      if (!image || !this.selection || !this.naturalSize.width || !this.naturalSize.height) return null;
      const imageRect = image.getBoundingClientRect();
      const scaleX = this.naturalSize.width / imageRect.width;
      const scaleY = this.naturalSize.height / imageRect.height;
      const sx = this.selection.x * scaleX;
      const sy = this.selection.y * scaleY;
      const sourceSize = this.selection.size * Math.min(scaleX, scaleY);
      const canvas = document.createElement('canvas');
      canvas.width = 600;
      canvas.height = 600;
      const ctx = canvas.getContext('2d');
      ctx.drawImage(image, sx, sy, sourceSize, sourceSize, 0, 0, 600, 600);
      return new Promise(resolve => canvas.toBlob(resolve, 'image/jpeg', 0.9));
    },
    resetSelection() {
      this.selection = null;
      this.selectionStart = null;
      this.selecting = false;
      this.previewBlob = null;
      this.revokePreviewUrl();
    },
    async saveCrop() {
      if (!this.canSaveCrop) return;
      this.savingCrop = true;
      this.errorMessage = '';
      this.statusMessage = '';
      try {
        const formData = new FormData();
        formData.append('image', this.previewBlob, 'group-photo-crop.jpg');
        if (this.makePrimary) {
          formData.append('makePrimary', 'true');
        }
        const response = await apiClient.post(`/clubmembers/image/${this.clubId}/${this.selectedMemberId}`, formData, {
          headers: { 'Content-Type': 'multipart/form-data' }
        });
        if (!response.data?.success) {
          throw new Error(response.data?.error || 'Ausschnitt konnte nicht gespeichert werden.');
        }
        this.$emit('member-image-updated', {
          memberId: this.selectedMemberId,
          payload: response.data
        });
        const member = this.members.find(entry => String(entry.id) === String(this.selectedMemberId));
        this.statusMessage = `Foto gespeichert${member ? ` für ${member.firstName} ${member.lastName}` : ''}.`;
        this.resetSelection();
      } catch (error) {
        this.errorMessage = error?.message || 'Ausschnitt konnte nicht gespeichert werden.';
      } finally {
        this.savingCrop = false;
      }
    },
    async saveSourcePhoto() {
      if (!this.sourceFile || this.currentSavedPhoto) return;
      this.savingSource = true;
      this.errorMessage = '';
      try {
        const formData = new FormData();
        formData.append('image', this.sourceFile);
        formData.append('title', this.sourceTitle || `Training ${new Date().toLocaleDateString('de-DE')}`);
        if (this.sourceTakenAt) formData.append('takenAt', this.sourceTakenAt);
        if (this.sourceDescription) formData.append('description', this.sourceDescription);
        const response = await apiClient.post(`/member-group-photos/${this.clubId}`, formData, {
          headers: { 'Content-Type': 'multipart/form-data' }
        });
        if (!response.data?.success) {
          throw new Error(response.data?.error || 'Gruppenfoto konnte nicht gespeichert werden.');
        }
        this.currentSavedPhoto = response.data.photo;
        this.savedPhotos = [response.data.photo, ...this.savedPhotos.filter(photo => photo.id !== response.data.photo.id)];
        this.statusMessage = 'Gruppenfoto gespeichert.';
      } catch (error) {
        this.errorMessage = error?.message || 'Gruppenfoto konnte nicht gespeichert werden.';
      } finally {
        this.savingSource = false;
      }
    },
    async loadSavedPhotos() {
      if (!this.clubId) return;
      this.loadingSaved = true;
      try {
        const response = await apiClient.get(`/member-group-photos/${this.clubId}`);
        this.savedPhotos = Array.isArray(response.data?.photos) ? response.data.photos : [];
      } catch (error) {
        this.errorMessage = 'Gespeicherte Gruppenfotos konnten nicht geladen werden.';
      } finally {
        this.loadingSaved = false;
      }
    },
    async openSavedPhoto(photo) {
      try {
        const apiPath = String(photo.imageUrl || '').replace(/^\/?api\//i, '').replace(/^\//, '');
        const response = await apiClient.get(apiPath, { responseType: 'blob' });
        this.revokeSourceUrl();
        this.revokePreviewUrl();
        this.sourceUrl = URL.createObjectURL(response.data);
        this.sourceFile = null;
        this.currentSavedPhoto = photo;
        this.sourceTitle = photo.title || '';
        this.sourceTakenAt = photo.takenAt ? String(photo.takenAt).slice(0, 10) : '';
        this.sourceDescription = photo.description || '';
        this.saveForLater = true;
        this.selection = null;
        this.previewBlob = null;
        this.statusMessage = `Gruppenfoto "${photo.title}" geöffnet.`;
        this.errorMessage = '';
      } catch (error) {
        this.errorMessage = 'Gruppenfoto konnte nicht geöffnet werden.';
      }
    },
    async deleteSavedPhoto(photo) {
      if (!window.confirm(`Gruppenfoto "${photo.title}" wirklich löschen? Bereits erstellte Mitgliedsfotos bleiben erhalten.`)) {
        return;
      }
      try {
        await apiClient.delete(`/member-group-photos/${this.clubId}/${photo.id}`);
        this.savedPhotos = this.savedPhotos.filter(entry => entry.id !== photo.id);
        if (this.currentSavedPhoto?.id === photo.id) {
          this.currentSavedPhoto = null;
          this.revokeSourceUrl();
          this.sourceUrl = '';
          this.sourceFile = null;
          this.resetSelection();
        }
      } catch (error) {
        this.errorMessage = 'Gruppenfoto konnte nicht gelöscht werden.';
      }
    },
    formatDate(value) {
      if (!value) return '';
      const date = new Date(value);
      if (Number.isNaN(date.getTime())) return '';
      return date.toLocaleDateString('de-DE');
    },
    revokeSourceUrl() {
      if (this.sourceUrl) {
        URL.revokeObjectURL(this.sourceUrl);
      }
    },
    revokePreviewUrl() {
      if (this.previewUrl) {
        URL.revokeObjectURL(this.previewUrl);
        this.previewUrl = '';
      }
    }
  }
};
</script>

<style scoped>
.group-photo-dialog {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.hidden-input {
  display: none;
}

.group-photo-actions,
.saved-photo-actions,
.save-source-fields {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  align-items: end;
}

.group-photo-hint,
.privacy-note {
  margin: 0.5rem 0 0;
  color: var(--text-muted);
  font-size: 0.88rem;
}

.privacy-note {
  color: #856404;
}

.saved-photo-list,
.save-source-panel {
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: 0.9rem;
  background: var(--background-light);
}

.saved-photo-list h3,
.crop-side h3 {
  margin: 0 0 0.7rem;
}

.saved-photo-grid {
  display: grid;
  gap: 0.7rem;
}

.saved-photo-item {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  align-items: center;
  padding: 0.7rem;
  border: 1px solid var(--border-color);
  border-radius: 8px;
  background: #fff;
}

.saved-photo-item span {
  display: block;
  color: var(--text-muted);
  font-size: 0.82rem;
}

.save-source-fields label,
.member-search {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.save-source-fields input,
.member-search input,
.member-select {
  border: 1px solid var(--border-color);
  border-radius: 6px;
  padding: 0.5rem;
}

.checkbox-line {
  display: flex;
  gap: 0.45rem;
  align-items: center;
}

.crop-workspace {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 1rem;
  align-items: start;
}

.crop-image-frame {
  position: relative;
  min-height: 320px;
  max-height: 68vh;
  border: 1px solid var(--border-color);
  border-radius: 8px;
  background: #111;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  touch-action: none;
  user-select: none;
}

.crop-image-frame img {
  max-width: 100%;
  max-height: 68vh;
  display: block;
}

.crop-selection {
  position: absolute;
  border: 2px solid #fff;
  outline: 9999px solid rgba(0, 0, 0, 0.38);
  box-shadow: 0 0 0 1px var(--primary-color);
  pointer-events: none;
}

.crop-side {
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: 0.9rem;
  background: #fff;
}

.crop-preview {
  width: 100%;
  aspect-ratio: 1;
  border: 1px dashed var(--border-color);
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  overflow: hidden;
  background: var(--background-light);
}

.crop-preview img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.btn-primary {
  background: var(--primary-color);
  color: var(--text-on-primary);
}

.danger-button {
  color: #b42318;
}

.warning-text {
  margin: 0.4rem 0 0;
  color: #856404;
  font-size: 0.84rem;
}

.status-message {
  color: #1f7a3f;
  font-weight: 600;
}

.error-message {
  color: #b42318;
  font-weight: 600;
}

@media (max-width: 900px) {
  .crop-workspace {
    grid-template-columns: 1fr;
  }

  .saved-photo-item {
    align-items: stretch;
    flex-direction: column;
  }
}
</style>
