const path = require('path');
const { authenticateToken, authenticateAdmin } = require(path.resolve(process.env.ROOT_PATH, './middleware/auth.js'));

class BaseController {
  constructor() {
    // Bind all methods to this to maintain context when used as route handlers
    const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(this))
      .filter(method => method !== 'constructor' && typeof this[method] === 'function');
    
    methods.forEach(method => {
      this[method] = this[method].bind(this);
    });

    // Store original methods before potentially wrapping them with auth
    this._originalMethods = {};
    methods.forEach(method => {
      this._originalMethods[method] = this[method];
    });
  }

  /**
   * Protects the given methods with authentication
   * @param {string|string[]} methodNames - The method names to protect
   * @returns {BaseController} The current instance for method chaining
   */
  protected(methodNames) {
    if (!Array.isArray(methodNames)) {
      methodNames = [methodNames];
    }

    methodNames.forEach(methodName => {
      if (typeof this[methodName] !== 'function') {
        throw new Error(`Method ${methodName} does not exist on ${this.constructor.name}`);
      }

      // Store original method if not already stored
      if (!this._originalMethods[methodName]) {
        this._originalMethods[methodName] = this[methodName];
      }

      // Replace the method with one that applies authentication first
      this[methodName] = (req, res, next) => {
        return authenticateToken(req, res, (err) => {
          if (err) return next(err);
          return this._originalMethods[methodName](req, res, next);
        });
      };
    });

    return this; // For method chaining
  }

  /**
   * Protects the given methods with admin authentication
   * @param {string|string[]} methodNames - The method names to protect. Use "*" to protect all methods.
   * @returns {BaseController} The current instance for method chaining
   */
  admin(methodNames) {
    if (!Array.isArray(methodNames)) {
      methodNames = [methodNames];
    }

    // If "*" is included, protect all methods
    if (methodNames.includes("*")) {
      const allMethods = Object.getOwnPropertyNames(Object.getPrototypeOf(this))
        .filter(method => 
          method !== 'constructor' && 
          method !== 'protected' && 
          method !== 'admin' &&
          typeof this[method] === 'function'
        );
      
      return this.admin(allMethods);
    }

    methodNames.forEach(methodName => {
      if (typeof this[methodName] !== 'function') {
        throw new Error(`Method ${methodName} does not exist on ${this.constructor.name}`);
      }

      // Store original method if not already stored
      if (!this._originalMethods[methodName]) {
        this._originalMethods[methodName] = this[methodName];
      }

      // Replace the method with one that applies admin authentication first
      this[methodName] = (req, res, next) => {
        return authenticateAdmin(req, res, (err) => {
          if (err) return next(err);
          return this._originalMethods[methodName](req, res, next);
        });
      };
    });

    return this; // For method chaining
  }
}

module.exports = BaseController; 