const AuditLog = require('../models/AuditLog');

// Map resource name → Mongoose model (lazy-loaded to avoid circular deps)
const getModel = (resource) => {
  const map = {
    User:   () => require('../models/User'),
    Config: () => require('../models/Config'),
    Admin:  () => require('../models/Admin')
  };
  return map[resource] ? map[resource]() : null;
};

// Remove internal Mongoose / sensitive fields for clean snapshots
const stripInternals = (obj) => {
  if (!obj) return null;
  const { __v, _id, createdAt, updatedAt, deleted, deletedAt, deletedBy,
          password, passwordHash, ...clean } = obj;
  return clean;
};

// Compute field-level diff between two plain objects
const computeDiff = (before, after) => {
  if (!before || !after) return null;
  const diff = {};
  const allKeys = new Set([...Object.keys(before), ...Object.keys(after)]);
  for (const key of allKeys) {
    if (JSON.stringify(before[key]) !== JSON.stringify(after[key])) {
      diff[key] = { before: before[key], after: after[key] };
    }
  }
  return Object.keys(diff).length > 0 ? diff : null;
};

/**
 * Middleware to automatically log admin actions with full before/after state.
 * Usage: router.post('/users', authenticateToken, auditLog('CREATE', 'User'), createUser);
 */
const auditLog = (action, resource) => {
  return async (req, res, next) => {
    const startTime = Date.now();

    // Capture before-state for UPDATE actions
    let beforeState = null;
    if (action === 'UPDATE' && req.params?.id) {
      try {
        const Model = getModel(resource);
        if (Model) {
          const doc = await Model.findById(req.params.id).lean();
          beforeState = stripInternals(doc);
        }
      } catch (_) { /* non-fatal */ }
    }

    const originalJson = res.json.bind(res);

    res.json = function(data) {
      const duration = Date.now() - startTime;
      const statusCode = res.statusCode;

      setImmediate(async () => {
        try {
          const isSuccess = data.success !== false && statusCode < 400;

          const logData = {
            action,
            resource,
            adminId:       req.user?.id || null,
            adminUsername: req.user?.username || 'unknown',
            ipAddress:     req.ip || req.connection?.remoteAddress,
            userAgent:     req.get('user-agent'),
            requestMethod: req.method,
            requestPath:   req.originalUrl?.split('?')[0],
            statusCode,
            duration,
            success: isSuccess
          };

          // Resource ID
          if (data.data?._id)      logData.resourceId = data.data._id;
          else if (req.params?.id) logData.resourceId = req.params.id;

          // Resource name
          if (data.data?.name)         logData.resourceName = data.data.name;
          else if (data.data?.username) logData.resourceName = data.data.username;

          // Before / after diff for UPDATE
          if (action === 'UPDATE') {
            const raw = data.data ? (data.data.toObject ? data.data.toObject() : data.data) : null;
            const afterState = stripInternals(raw);
            logData.before  = beforeState;
            logData.after   = afterState;
            logData.changes = computeDiff(beforeState, afterState);
          }

          // IMPORT: store import statistics in metadata
          if (action === 'IMPORT' && data.results) {
            logData.metadata    = {
              imported: data.results.imported,
              skipped:  data.results.skipped,
              errors:   data.results.errors?.length || 0
            };
            logData.resourceName = `Import (${data.results.imported} importiert, ${data.results.skipped} übersprungen)`;
          }

          // EXPORT: store format
          if (action === 'EXPORT') {
            const fmt = req.query.format?.toUpperCase() || 'JSON';
            logData.metadata     = { format: fmt };
            logData.resourceName = `Export (${fmt})`;
          }

          // BULK operations: store count + affected fields
          if (action === 'BULK_UPDATE' || action === 'BULK_DELETE') {
            const count = Array.isArray(req.body?.ids) ? req.body.ids.length : (data.data?.matched || 0);
            logData.metadata = {
              count,
              ...(req.body?.updates ? { fields: Object.keys(req.body.updates) } : {})
            };
            logData.resourceName = `${action === 'BULK_DELETE' ? 'Massen-Löschung' : 'Massen-Update'} (${count} Einträge)`;
          }

          // Error message
          if (!isSuccess && data.message) {
            logData.errorMessage = data.message;
          }

          await AuditLog.log(logData);
        } catch (error) {
          const logger = require('../utils/logger');
          logger.error('Audit logging error:', error);
        }
      });

      return originalJson(data);
    };

    next();
  };
};

/**
 * Log authentication attempts (success and failure)
 */
const auditAuth = async (req, isSuccess, username, errorMessage = null) => {
  try {
    await AuditLog.log({
      action:        isSuccess ? 'LOGIN' : 'LOGIN_FAILED',
      resource:      'Admin',
      adminUsername: username,
      ipAddress:     req.ip || req.connection?.remoteAddress,
      userAgent:     req.get('user-agent'),
      requestMethod: req.method,
      requestPath:   req.originalUrl?.split('?')[0],
      statusCode:    isSuccess ? 200 : 401,
      success:       isSuccess,
      errorMessage
    });
  } catch (error) {
    const logger = require('../utils/logger');
    logger.error('Auth audit logging error:', error);
  }
};

module.exports = { auditLog, auditAuth };
