const User = require('../models/User');
const { geocodeAddress } = require('../utils/geocode');
const logger = require('../utils/logger');

const getAllUsers = async (req, res) => {
  try {
    // Pagination parameters
    const page = Math.max(1, parseInt(req.query.page) || 1);
    const limit = Math.min(100, Math.max(1, parseInt(req.query.limit) || 50)); // Max 100 items per page
    const skip = (page - 1) * limit;

    // Optional filters
    const filter = {};
    if (req.query.available !== undefined) {
      filter.available = req.query.available === 'true';
    }
    if (req.query.type) {
      filter.type = req.query.type;
    }
    if (req.query.search) {
      // Text search using the text index
      filter.$text = { $search: req.query.search };
    }

    // Execute query with pagination
    const [users, total] = await Promise.all([
      User.find(filter)
        .sort(req.query.search ? { score: { $meta: 'textScore' } } : { name: 1 })
        .skip(skip)
        .limit(limit)
        .select('-__v'), // Exclude version key
      User.countDocuments(filter)
    ]);

    res.json({
      success: true,
      data: users,
      pagination: {
        page,
        limit,
        total,
        pages: Math.ceil(total / limit),
        hasMore: page < Math.ceil(total / limit)
      }
    });
  } catch (error) {
    logger.error('Fehler beim Abrufen der Benutzer:', error);
    res.status(500).json({
      success: false,
      message: 'Fehler beim Abrufen der Benutzer'
    });
  }
};

const getUserById = async (req, res) => {
  try {
    const user = await User.findById(req.params.id);
    if (!user) {
      return res.status(404).json({
        success: false,
        message: 'Benutzer nicht gefunden'
      });
    }
    res.json({
      success: true,
      data: user
    });
  } catch (error) {
    logger.error('Fehler beim Abrufen des Benutzers:', error);
    res.status(500).json({
      success: false,
      message: 'Fehler beim Abrufen des Benutzers'
    });
  }
};

const createUser = async (req, res) => {
  try {
    const payload = { ...req.body };

    let geoWarning = null;
    if (!payload.gps && payload.address) {
      const coords = await geocodeAddress(payload.address);
      if (coords) {
        payload.gps = coords;
      } else {
        geoWarning = 'Adresse konnte nicht geokodiert werden. GPS kann manuell über die Karte gesetzt werden.';
        logger.warn(`Geocoding fehlgeschlagen für Adresse: ${payload.address}`);
      }
    }

    const user = new User(payload);
    await user.save();
    res.status(201).json({
      success: true,
      data: user,
      ...(geoWarning && { warning: geoWarning })
    });
  } catch (error) {
    logger.error('Fehler beim Erstellen des Benutzers:', error);
    res.status(400).json({
      success: false,
      message: error.message || 'Fehler beim Erstellen des Benutzers'
    });
  }
};

const updateUser = async (req, res) => {
  try {
    const existing = await User.findById(req.params.id);
    if (!existing) {
      return res.status(404).json({
        success: false,
        message: 'Benutzer nicht gefunden'
      });
    }

    const updateData = { ...req.body };
    const addressChanged = typeof updateData.address === 'string'
      && updateData.address.trim() !== existing.address;
    const hasGpsInRequest = updateData.gps
      && updateData.gps.lat !== undefined
      && updateData.gps.lng !== undefined;

    let geoWarning = null;
    if (addressChanged && !hasGpsInRequest) {
      const coords = await geocodeAddress(updateData.address);
      if (coords) {
        updateData.gps = coords;
      } else {
        geoWarning = 'Adresse konnte nicht geokodiert werden. GPS kann manuell über die Karte gesetzt werden.';
        logger.warn(`Geocoding fehlgeschlagen für Adresse: ${updateData.address}`);
      }
    }

    const user = await User.findByIdAndUpdate(
      req.params.id,
      updateData,
      { new: true, runValidators: true }
    );
    if (!user) {
      return res.status(404).json({
        success: false,
        message: 'Benutzer nicht gefunden'
      });
    }
    res.json({
      success: true,
      data: user,
      ...(geoWarning && { warning: geoWarning })
    });
  } catch (error) {
    logger.error('Fehler beim Aktualisieren des Benutzers:', error);
    res.status(400).json({
      success: false,
      message: error.message || 'Fehler beim Aktualisieren des Benutzers'
    });
  }
};

const deleteUser = async (req, res) => {
  try {
    // Soft delete - mark as deleted instead of removing from database
    const user = await User.findByIdAndUpdate(
      req.params.id,
      {
        deleted: true,
        deletedAt: new Date(),
        deletedBy: req.user?.id || null
      },
      { new: true }
    );
    
    if (!user) {
      return res.status(404).json({
        success: false,
        message: 'Benutzer nicht gefunden'
      });
    }
    
    logger.info(`User soft-deleted: ${user.name} (ID: ${user._id}) by admin ${req.user?.username || 'unknown'}`);
    
    res.json({
      success: true,
      message: 'Benutzer erfolgreich gelöscht',
      data: user
    });
  } catch (error) {
    logger.error('Fehler beim Löschen des Benutzers:', error);
    res.status(500).json({
      success: false,
      message: 'Fehler beim Löschen des Benutzers'
    });
  }
};

const restoreUser = async (req, res) => {
  try {
    // Restore soft-deleted user
    const user = await User.findOneAndUpdate(
      { _id: req.params.id, deleted: true },
      {
        deleted: false,
        deletedAt: null,
        deletedBy: null
      },
      { new: true }
    ).setOptions({ includeDeleted: true });
    
    if (!user) {
      return res.status(404).json({
        success: false,
        message: 'Gelöschter Benutzer nicht gefunden'
      });
    }
    
    logger.info(`User restored: ${user.name} (ID: ${user._id}) by admin ${req.user?.username || 'unknown'}`);
    
    res.json({
      success: true,
      message: 'Benutzer erfolgreich wiederhergestellt',
      data: user
    });
  } catch (error) {
    logger.error('Fehler beim Wiederherstellen des Benutzers:', error);
    res.status(500).json({
      success: false,
      message: 'Fehler beim Wiederherstellen des Benutzers'
    });
  }
};

const getDeletedUsers = async (req, res) => {
  try {
    // Get only deleted users
    const users = await User.find({ deleted: true })
      .sort({ deletedAt: -1 })
      .populate('deletedBy', 'username')
      .setOptions({ includeDeleted: true });
    
    res.json({
      success: true,
      data: users,
      count: users.length
    });
  } catch (error) {
    logger.error('Fehler beim Abrufen gelöschter Benutzer:', error);
    res.status(500).json({
      success: false,
      message: 'Fehler beim Abrufen gelöschter Benutzer'
    });
  }
};

const getPublicUsers = async (req, res) => {
  try {
    // Pagination parameters
    const page = Math.max(1, parseInt(req.query.page) || 1);
    const limit = Math.min(100, Math.max(1, parseInt(req.query.limit) || 50));
    const skip = (page - 1) * limit;

    // Filter for available users only + optional type filter
    const filter = { available: true };
    if (req.query.type) {
      filter.type = req.query.type;
    }
    if (req.query.search) {
      filter.$text = { $search: req.query.search };
    }

    // Execute query with pagination
    const [users, total] = await Promise.all([
      User.find(filter)
        .sort(req.query.search ? { score: { $meta: 'textScore' } } : { name: 1 })
        .skip(skip)
        .limit(limit)
        .select('-__v'),
      User.countDocuments(filter)
    ]);

    res.json({
      success: true,
      data: users,
      pagination: {
        page,
        limit,
        total,
        pages: Math.ceil(total / limit),
        hasMore: page < Math.ceil(total / limit)
      }
    });
  } catch (error) {
    logger.error('Fehler beim Abrufen der öffentlichen Benutzer:', error);
    res.status(500).json({
      success: false,
      message: 'Fehler beim Abrufen der öffentlichen Benutzer'
    });
  }
};

const updateAvailability = async (req, res) => {
  try {
    const { id } = req.params;
    const { available } = req.body;

    if (typeof available !== 'boolean') {
      return res.status(400).json({
        success: false,
        message: 'Verfügbarkeit muss ein Boolean-Wert sein'
      });
    }

    const user = await User.findByIdAndUpdate(
      id,
      { available },
      { new: true, runValidators: true }
    );

    if (!user) {
      return res.status(404).json({
        success: false,
        message: 'Benutzer nicht gefunden'
      });
    }

    res.json({
      success: true,
      data: user
    });
  } catch (error) {
    logger.error('Fehler beim Aktualisieren der Verfügbarkeit:', error);
    res.status(500).json({
      success: false,
      message: 'Fehler beim Aktualisieren der Verfügbarkeit'
    });
  }
};

const updateGPS = async (req, res) => {
  try {
    const { id } = req.params;
    const { lat, lng } = req.body;

    // Validate GPS coordinates
    if (lat === undefined || lng === undefined) {
      return res.status(400).json({
        success: false,
        message: 'Latitude und Longitude sind erforderlich'
      });
    }

    const latNum = parseFloat(lat);
    const lngNum = parseFloat(lng);

    if (isNaN(latNum) || isNaN(lngNum)) {
      return res.status(400).json({
        success: false,
        message: 'Ungültige GPS-Koordinaten'
      });
    }

    if (latNum < -90 || latNum > 90 || lngNum < -180 || lngNum > 180) {
      return res.status(400).json({
        success: false,
        message: 'GPS-Koordinaten außerhalb des gültigen Bereichs'
      });
    }

    const user = await User.findByIdAndUpdate(
      id,
      { gps: { lat: latNum, lng: lngNum } },
      { new: true, runValidators: true }
    );

    if (!user) {
      return res.status(404).json({
        success: false,
        message: 'Benutzer nicht gefunden'
      });
    }

    res.json({
      success: true,
      data: user
    });
  } catch (error) {
    logger.error('Fehler beim Aktualisieren der GPS-Koordinaten:', error);
    res.status(500).json({
      success: false,
      message: 'Fehler beim Aktualisieren der GPS-Koordinaten'
    });
  }
};

const exportUsers = async (req, res) => {
  try {
    const format = req.query.format || 'json';
    const users = await User.find({ deleted: { $ne: true } })
      .sort({ name: 1 })
      .select('-__v -passwordHash -deleted -deletedAt -deletedBy');

    if (format === 'csv') {
      const escapeCell = (val) => {
        if (val == null) return '';
        const str = String(val);
        return str.includes(',') || str.includes('"') || str.includes('\n')
          ? `"${str.replace(/"/g, '""')}"`
          : str;
      };

      const csv = [
        ['Name', 'Adresse', 'Telefon', 'Festnetz', 'E-Mail', 'Typ', 'Verfügbar', 'Latitude', 'Longitude'].join(','),
        ...users.map(user => [
          escapeCell(user.name),
          escapeCell(user.address),
          escapeCell(user.phone),
          escapeCell(user.landline),
          escapeCell(user.email),
          escapeCell(user.type),
          user.available ? 'Ja' : 'Nein',
          user.gps?.lat ?? '',
          user.gps?.lng ?? ''
        ].join(','))
      ].join('\n');

      res.setHeader('Content-Type', 'text/csv; charset=utf-8');
      res.setHeader('Content-Disposition',
        `attachment; filename="nachsuchenfuehrer_export_${new Date().toISOString().slice(0, 10)}.csv"`);
      return res.send('\uFEFF' + csv); // BOM für korrekte UTF-8-Darstellung in Excel
    }

    // JSON format – sauberes Import-Format ohne MongoDB-interne Felder
    const exportData = users.map(u => ({
      name:      u.name,
      address:   u.address,
      phone:     u.phone,
      landline:  u.landline || null,
      email:     u.email    || null,
      type:      u.type,
      available: u.available,
      ...(u.gps?.lat != null && u.gps?.lng != null ? { gps: { lat: u.gps.lat, lng: u.gps.lng } } : {})
    }));

    res.setHeader('Content-Disposition',
      `attachment; filename="nachsuchenfuehrer_export_${new Date().toISOString().slice(0, 10)}.json"`);
    res.json({
      exportedAt: new Date().toISOString(),
      count: exportData.length,
      users: exportData
    });
  } catch (error) {
    logger.error('Fehler beim Exportieren der Benutzer:', error);
    res.status(500).json({
      success: false,
      message: 'Fehler beim Exportieren der Benutzer'
    });
  }
};

/**
 * Import users from a previously exported JSON file.
 * Accepts { users: [...] } – same format as the JSON export.
 * Existing users (matched by name + address) are skipped unless ?overwrite=true.
 */
const importUsers = async (req, res) => {
  try {
    const { users: importList } = req.body;

    if (!Array.isArray(importList) || importList.length === 0) {
      return res.status(400).json({
        success: false,
        message: 'Kein gültiges Import-Format. Erwartet: { users: [...] }'
      });
    }

    const overwrite = req.query.overwrite === 'true';

    const results = { imported: 0, skipped: 0, errors: [] };

    for (const entry of importList) {
      const { name, address, phone, type } = entry;
      if (!name || !address || !phone || !type) {
        results.errors.push({ entry: entry.name || '(unbekannt)', reason: 'Pflichtfelder fehlen (name, address, phone, type)' });
        continue;
      }

      try {
        const existing = await User.findOne({ name: name.trim(), deleted: { $ne: true } });
        if (existing && !overwrite) {
          results.skipped++;
          continue;
        }

        const payload = {
          name:      name.trim(),
          address:   address.trim(),
          phone:     phone.trim(),
          landline:  entry.landline || null,
          email:     entry.email    || null,
          type:      type.trim(),
          available: entry.available !== undefined ? Boolean(entry.available) : true
        };

        if (entry.gps?.lat != null && entry.gps?.lng != null) {
          payload.gps = { lat: Number(entry.gps.lat), lng: Number(entry.gps.lng) };
        } else {
          // GPS aus Adresse ermitteln
          const coords = await geocodeAddress(payload.address);
          if (coords) payload.gps = coords;
        }

        if (existing && overwrite) {
          await User.findByIdAndUpdate(existing._id, payload, { runValidators: true });
        } else {
          await User.create(payload);
        }
        results.imported++;
      } catch (err) {
        results.errors.push({ entry: entry.name || '(unbekannt)', reason: err.message });
      }
    }

    logger.info(`Import abgeschlossen: ${results.imported} importiert, ${results.skipped} übersprungen, ${results.errors.length} Fehler`);
    res.json({
      success: true,
      message: `Import abgeschlossen: ${results.imported} importiert, ${results.skipped} übersprungen, ${results.errors.length} Fehler`,
      results
    });
  } catch (error) {
    logger.error('Fehler beim Importieren der Benutzer:', error);
    res.status(500).json({
      success: false,
      message: 'Fehler beim Importieren der Benutzer'
    });
  }
};

/**
 * Bulk update users - update multiple users at once
 */
const bulkUpdateUsers = async (req, res) => {
  try {
    const { ids, updates } = req.body;

    if (!ids || !Array.isArray(ids) || ids.length === 0) {
      return res.status(400).json({
        success: false,
        message: 'IDs-Array ist erforderlich'
      });
    }

    if (!updates || typeof updates !== 'object') {
      return res.status(400).json({
        success: false,
        message: 'Updates-Objekt ist erforderlich'
      });
    }

    // Prevent updating sensitive fields
    const allowedFields = ['available', 'type', 'phone', 'address'];
    const updateFields = {};
    for (const [key, value] of Object.entries(updates)) {
      if (allowedFields.includes(key)) {
        updateFields[key] = value;
      }
    }

    if (Object.keys(updateFields).length === 0) {
      return res.status(400).json({
        success: false,
        message: 'Keine gültigen Update-Felder angegeben'
      });
    }

    // Perform bulk update
    const result = await User.updateMany(
      { _id: { $in: ids } },
      { $set: updateFields }
    );

    logger.info(`Bulk update: ${result.modifiedCount} users updated by admin ${req.user?.username || 'unknown'}`);

    res.json({
      success: true,
      message: `${result.modifiedCount} Benutzer erfolgreich aktualisiert`,
      data: {
        matched: result.matchedCount,
        modified: result.modifiedCount
      }
    });
  } catch (error) {
    logger.error('Fehler beim Bulk-Update:', error);
    res.status(500).json({
      success: false,
      message: 'Fehler beim Bulk-Update'
    });
  }
};

/**
 * Bulk delete users - soft delete multiple users at once
 */
const bulkDeleteUsers = async (req, res) => {
  try {
    const { ids } = req.body;

    if (!ids || !Array.isArray(ids) || ids.length === 0) {
      return res.status(400).json({
        success: false,
        message: 'IDs-Array ist erforderlich'
      });
    }

    // Soft delete all users
    const result = await User.updateMany(
      { _id: { $in: ids } },
      {
        $set: {
          deleted: true,
          deletedAt: new Date(),
          deletedBy: req.user?.id || null
        }
      }
    );

    logger.info(`Bulk delete: ${result.modifiedCount} users deleted by admin ${req.user?.username || 'unknown'}`);

    res.json({
      success: true,
      message: `${result.modifiedCount} Benutzer erfolgreich gelöscht`,
      data: {
        matched: result.matchedCount,
        modified: result.modifiedCount
      }
    });
  } catch (error) {
    logger.error('Fehler beim Bulk-Delete:', error);
    res.status(500).json({
      success: false,
      message: 'Fehler beim Bulk-Delete'
    });
  }
};

const uploadUserPhoto = async (req, res) => {
  try {
    const { photo } = req.body;
    if (!photo || typeof photo !== 'string' || !photo.startsWith('data:image/')) {
      return res.status(400).json({ success: false, message: 'Ungültiges Bildformat' });
    }
    const user = await User.findOne({ _id: req.params.id, deleted: { $ne: true } });
    if (!user) {
      return res.status(404).json({ success: false, message: 'Benutzer nicht gefunden' });
    }
    user.photo = photo;
    await user.save();
    res.json({ success: true, data: { photo: user.photo } });
  } catch (error) {
    logger.error('Fehler beim Foto-Upload:', error);
    res.status(500).json({ success: false, message: 'Fehler beim Foto-Upload' });
  }
};

const deleteUserPhoto = async (req, res) => {
  try {
    const user = await User.findOne({ _id: req.params.id, deleted: { $ne: true } });
    if (!user) {
      return res.status(404).json({ success: false, message: 'Benutzer nicht gefunden' });
    }
    user.photo = null;
    await user.save();
    res.json({ success: true });
  } catch (error) {
    logger.error('Fehler beim Löschen des Fotos:', error);
    res.status(500).json({ success: false, message: 'Fehler beim Löschen des Fotos' });
  }
};

module.exports = {
  getAllUsers,
  getUserById,
  createUser,
  updateUser,
  deleteUser,
  restoreUser,
  getDeletedUsers,
  getPublicUsers,
  updateAvailability,
  updateGPS,
  exportUsers,
  importUsers,
  bulkUpdateUsers,
  bulkDeleteUsers,
  uploadUserPhoto,
  deleteUserPhoto
};
