import fs from 'fs';
import path from 'path';
import sharp from 'sharp';
import MemberGroupPhoto from '../models/MemberGroupPhoto.js';
import { checkAccess, getUserByToken } from '../utils/userUtils.js';

class MemberGroupPhotoService {
  async list(userToken, clubId) {
    await checkAccess(userToken, clubId);
    const photos = await MemberGroupPhoto.findAll({
      where: { clubId },
      order: [['createdAt', 'DESC']]
    });
    return photos.map(photo => this.mapPhoto(photo));
  }

  async create(userToken, clubId, file, payload = {}) {
    await checkAccess(userToken, clubId);
    if (!file?.buffer) {
      return { status: 400, response: { success: false, error: 'No image provided' } };
    }
    if (!String(file.mimetype || '').startsWith('image/')) {
      return { status: 400, response: { success: false, error: 'File must be an image' } };
    }

    const user = await getUserByToken(userToken);
    const title = this.sanitizeText(payload.title, 255) || this.defaultTitle();
    const description = this.sanitizeText(payload.description, 5000) || null;
    const takenAt = this.parseDate(payload.takenAt);

    let photo = null;
    try {
      photo = await MemberGroupPhoto.create({
        clubId,
        title,
        description,
        fileName: 'pending.jpg',
        originalFileName: this.sanitizeText(file.originalname, 255) || null,
        mimeType: 'image/jpeg',
        fileSize: file.size || file.buffer.length || null,
        takenAt,
        createdByUserId: user?.id || null
      });

      const directory = await this.ensurePhotoDirectory(clubId);
      const fileName = `${photo.id}.jpg`;
      const targetPath = path.join(directory, fileName);
      const info = await sharp(file.buffer)
        .rotate()
        .resize(2400, 2400, { fit: 'inside', withoutEnlargement: true })
        .jpeg({ quality: 88 })
        .toFile(targetPath);

      await photo.update({
        fileName,
        fileSize: info.size || file.size || file.buffer.length || null,
        width: info.width || null,
        height: info.height || null
      });

      return { status: 200, response: { success: true, photo: this.mapPhoto(photo) } };
    } catch (error) {
      if (photo?.id) {
        await photo.destroy().catch(() => {});
        await fs.promises.unlink(this.getPhotoPath(clubId, `${photo.id}.jpg`)).catch(() => {});
      }
      console.error('[MemberGroupPhotoService.create] error:', error);
      return { status: 500, response: { success: false, error: 'Failed to save group photo' } };
    }
  }

  async update(userToken, clubId, photoId, payload = {}) {
    await checkAccess(userToken, clubId);
    const photo = await this.findPhoto(clubId, photoId);
    if (!photo) {
      return { status: 404, response: { success: false, error: 'Group photo not found' } };
    }

    const updates = {};
    if (payload.title !== undefined) {
      updates.title = this.sanitizeText(payload.title, 255) || photo.title;
    }
    if (payload.description !== undefined) {
      updates.description = this.sanitizeText(payload.description, 5000) || null;
    }
    if (payload.takenAt !== undefined) {
      updates.takenAt = this.parseDate(payload.takenAt);
    }

    await photo.update(updates);
    return { status: 200, response: { success: true, photo: this.mapPhoto(photo) } };
  }

  async remove(userToken, clubId, photoId) {
    await checkAccess(userToken, clubId);
    const photo = await this.findPhoto(clubId, photoId);
    if (!photo) {
      return { status: 404, response: { success: false, error: 'Group photo not found' } };
    }

    const imagePath = this.getPhotoPath(clubId, photo.fileName);
    await photo.destroy();
    await fs.promises.unlink(imagePath).catch((error) => {
      console.warn('[MemberGroupPhotoService.remove] file removal failed:', error.message || error);
    });

    return { status: 200, response: { success: true } };
  }

  async getImage(userToken, clubId, photoId) {
    await checkAccess(userToken, clubId);
    const photo = await this.findPhoto(clubId, photoId);
    if (!photo) {
      return { status: 404, error: 'Group photo not found' };
    }

    const imagePath = this.getPhotoPath(clubId, photo.fileName);
    if (!fs.existsSync(imagePath)) {
      return { status: 404, error: 'Group photo file not found' };
    }

    return { status: 200, imagePath: path.resolve(imagePath) };
  }

  async findPhoto(clubId, photoId) {
    return MemberGroupPhoto.findOne({ where: { id: photoId, clubId } });
  }

  mapPhoto(photo) {
    const data = typeof photo?.toJSON === 'function' ? photo.toJSON() : photo;
    return {
      id: data.id,
      clubId: data.clubId,
      title: data.title,
      description: data.description || '',
      fileName: data.fileName,
      originalFileName: data.originalFileName || '',
      mimeType: data.mimeType || 'image/jpeg',
      fileSize: data.fileSize || null,
      width: data.width || null,
      height: data.height || null,
      takenAt: data.takenAt || null,
      createdByUserId: data.createdByUserId || null,
      createdAt: data.createdAt || null,
      updatedAt: data.updatedAt || null,
      imageUrl: `/api/member-group-photos/${data.clubId}/${data.id}/image?t=${new Date(data.updatedAt || Date.now()).getTime()}`
    };
  }

  async ensurePhotoDirectory(clubId) {
    const directory = path.join('images', 'member-group-photos', String(clubId));
    await fs.promises.mkdir(directory, { recursive: true });
    return directory;
  }

  getPhotoPath(clubId, fileName) {
    return path.join('images', 'member-group-photos', String(clubId), fileName);
  }

  sanitizeText(value, maxLength) {
    const text = String(value || '').trim();
    if (!text) return '';
    return text.slice(0, maxLength);
  }

  parseDate(value) {
    if (!value) return null;
    const date = new Date(value);
    return Number.isNaN(date.getTime()) ? null : date;
  }

  defaultTitle() {
    return `Gruppenfoto ${new Date().toLocaleDateString('de-DE')}`;
  }
}

export default new MemberGroupPhotoService();
