/**
 * DSS Router
 *
 * Centralized hash-based routing with guards, lifecycle hooks, and
 * declarative route definitions for enterprise-grade navigation management.
 *
 * @module router
 */

import { notifyError, ErrorCode } from './messaging.js';

/**
 * Route configuration object
 * @typedef {Object} RouteConfig
 * @property {string} path - Route path (e.g., '/dashboard', '/projects')
 * @property {string} name - Route name for programmatic navigation
 * @property {Function} handler - Route handler function
 * @property {Function} [beforeEnter] - Guard called before entering route
 * @property {Function} [afterEnter] - Hook called after entering route
 * @property {Function} [onLeave] - Hook called when leaving route
 * @property {Object} [meta] - Route metadata
 */

/**
 * Router class for centralized route management
 */
class Router {
  constructor() {
    this.routes = new Map();
    this.currentRoute = null;
    this.previousRoute = null;
    this.defaultRoute = 'projects'; // Updated default for new architecture
    this.isNavigating = false;

    // Bind handlers
    this.handleHashChange = this.handleHashChange.bind(this);
    this.handlePopState = this.handlePopState.bind(this);
  }

  /**
   * Initialize the router
   */
  init() {
    // Register new feature module routes
    this.registerAll([
      {
        path: '/projects',
        name: 'Projects',
        handler: () => this.loadModule('dss-projects-module', () => import('../modules/projects/ProjectsModule.js'))
      },
      {
        path: '/config',
        name: 'Configuration',
        handler: () => this.loadModule('dss-config-module', () => import('../modules/config/ConfigModule.js'))
      },
      {
        path: '/components',
        name: 'Components',
        handler: () => this.loadModule('dss-components-module', () => import('../modules/components/ComponentsModule.js'))
      },
      {
        path: '/translations',
        name: 'Translations',
        handler: () => this.loadModule('dss-translations-module', () => import('../modules/translations/TranslationsModule.js'))
      },
      {
        path: '/discovery',
        name: 'Discovery',
        handler: () => this.loadModule('dss-discovery-module', () => import('../modules/discovery/DiscoveryModule.js'))
      },
      {
        path: '/admin',
        name: 'Admin',
        handler: () => this.loadModule('dss-admin-module', () => import('../modules/admin/AdminModule.js'))
      }
    ]);

    // Listen for hash changes
    window.addEventListener('hashchange', this.handleHashChange);
    window.addEventListener('popstate', this.handlePopState);

    // Handle initial route
    this.handleHashChange();
  }

  /**
   * Helper to load dynamic modules into the stage
   * @param {string} tagName - Custom element tag name
   * @param {Function} importFn - Dynamic import function
   */
  async loadModule(tagName, importFn) {
    try {
      // 1. Load the module file
      await importFn();

      // 2. Update the stage content
      const stageContent = document.querySelector('#stage-workdesk-content');
      if (stageContent) {
        stageContent.innerHTML = '';
        const element = document.createElement(tagName);
        stageContent.appendChild(element);
      }
    } catch (error) {
      console.error(`Failed to load module ${tagName}:`, error);
      notifyError(`Failed to load module`, ErrorCode.SYSTEM_UNEXPECTED);
    }
  }

  /**
   * Register a route
   * @param {RouteConfig} config - Route configuration
   */
  register(config) {
    if (!config.path) {
      throw new Error('Route path is required');
    }

    if (!config.handler) {
      throw new Error('Route handler is required');
    }

    // Normalize path (remove leading slash for hash routing)
    const path = config.path.replace(/^\//, '');

    this.routes.set(path, {
      path,
      name: config.name || path,
      handler: config.handler,
      beforeEnter: config.beforeEnter || null,
      afterEnter: config.afterEnter || null,
      onLeave: config.onLeave || null,
      meta: config.meta || {},
    });

    return this;
  }

  /**
   * Register multiple routes
   * @param {RouteConfig[]} routes - Array of route configurations
   */
  registerAll(routes) {
    routes.forEach(route => this.register(route));
    return this;
  }

  /**
   * Set default route
   * @param {string} path - Default route path
   */
  setDefaultRoute(path) {
    this.defaultRoute = path.replace(/^\//, '');
    return this;
  }

  /**
   * Navigate to a route
   * @param {string} path - Route path or name
   * @param {Object} [options] - Navigation options
   * @param {boolean} [options.replace] - Replace history instead of push
   * @param {Object} [options.state] - State to pass to route
   */
  navigate(path, options = {}) {
    const normalizedPath = path.replace(/^\//, '');

    // Update hash
    if (options.replace) {
      window.location.replace(`#${normalizedPath}`);
    } else {
      window.location.hash = normalizedPath;
    }

    return this;
  }

  /**
   * Navigate to a route by name
   * @param {string} name - Route name
   * @param {Object} [options] - Navigation options
   */
  navigateByName(name, options = {}) {
    const route = Array.from(this.routes.values()).find(r => r.name === name);

    if (!route) {
      notifyError(`Route not found: ${name}`, ErrorCode.SYSTEM_UNEXPECTED);
      return this;
    }

    return this.navigate(route.path, options);
  }

  /**
   * Handle hash change event
   */
  async handleHashChange() {
    if (this.isNavigating) return;

    this.isNavigating = true;

    try {
      // Get path from hash
      let path = window.location.hash.replace(/^#/, '') || this.defaultRoute;

      // Extract route and params
      const { routePath, params } = this.parseRoute(path);

      // Find route
      const route = this.routes.get(routePath);

      if (!route) {
        console.warn(`Route not registered: ${routePath}, falling back to default`);
        this.navigate(this.defaultRoute, { replace: true });
        return;
      }

      // Call onLeave hook for previous route
      if (this.currentRoute && this.currentRoute.onLeave) {
        await this.callHook(this.currentRoute.onLeave, { from: this.currentRoute, to: route });
      }

      // Call beforeEnter guard
      if (route.beforeEnter) {
        const canEnter = await this.callGuard(route.beforeEnter, { route, params });

        if (!canEnter) {
          // Guard rejected, stay on current route or go to default
          if (this.currentRoute) {
            this.navigate(this.currentRoute.path, { replace: true });
          } else {
            this.navigate(this.defaultRoute, { replace: true });
          }
          return;
        }
      }

      // Update route tracking
      this.previousRoute = this.currentRoute;
      this.currentRoute = route;

      // Call route handler
      await route.handler({ route, params, router: this });

      // Call afterEnter hook
      if (route.afterEnter) {
        await this.callHook(route.afterEnter, { route, params, from: this.previousRoute });
      }

      // Emit route change event
      this.emitRouteChange(route, params);

    } catch (error) {
      console.error('Router navigation error:', error);
      notifyError('Navigation failed', ErrorCode.SYSTEM_UNEXPECTED, {
        path: window.location.hash,
        error: error.message,
      });
    } finally {
      this.isNavigating = false;
    }
  }

  /**
   * Handle popstate event (browser back/forward)
   */
  handlePopState(event) {
    this.handleHashChange();
  }

  /**
   * Parse route path and extract params
   * @param {string} path - Route path
   * @returns {Object} Route path and params
   */
  parseRoute(path) {
    // For now, simple implementation - just return the path
    // Can be extended to support params like '/projects/:id'
    const [routePath, queryString] = path.split('?');

    const params = {};
    if (queryString) {
      new URLSearchParams(queryString).forEach((value, key) => {
        params[key] = value;
      });
    }

    return { routePath, params };
  }

  /**
   * Call a route guard
   * @param {Function} guard - Guard function
   * @param {Object} context - Guard context
   * @returns {Promise<boolean>} Whether navigation should proceed
   */
  async callGuard(guard, context) {
    try {
      const result = await guard(context);
      return result !== false; // Undefined or true = proceed
    } catch (error) {
      console.error('Route guard error:', error);
      return false;
    }
  }

  /**
   * Call a lifecycle hook
   * @param {Function} hook - Hook function
   * @param {Object} context - Hook context
   */
  async callHook(hook, context) {
    try {
      await hook(context);
    } catch (error) {
      console.error('Route hook error:', error);
    }
  }

  /**
   * Emit route change event
   * @param {Object} route - Current route
   * @param {Object} params - Route params
   */
  emitRouteChange(route, params) {
    window.dispatchEvent(new CustomEvent('route-changed', {
      detail: {
        route,
        params,
        previous: this.previousRoute,
      }
    }));
  }

  /**
   * Get current route
   * @returns {Object|null} Current route config
   */
  getCurrentRoute() {
    return this.currentRoute;
  }

  /**
   * Get previous route
   * @returns {Object|null} Previous route config
   */
  getPreviousRoute() {
    return this.previousRoute;
  }

  /**
   * Check if a route exists
   * @param {string} path - Route path
   * @returns {boolean} Whether route exists
   */
  hasRoute(path) {
    const normalizedPath = path.replace(/^\//, '');
    return this.routes.has(normalizedPath);
  }

  /**
   * Get route by path
   * @param {string} path - Route path
   * @returns {Object|null} Route config
   */
  getRoute(path) {
    const normalizedPath = path.replace(/^\//, '');
    return this.routes.get(normalizedPath) || null;
  }

  /**
   * Get all routes
   * @returns {Array} All registered routes
   */
  getAllRoutes() {
    return Array.from(this.routes.values());
  }

  /**
   * Go back in history
   */
  back() {
    window.history.back();
    return this;
  }

  /**
   * Go forward in history
   */
  forward() {
    window.history.forward();
    return this;
  }

  /**
   * Destroy the router (cleanup)
   */
  destroy() {
    window.removeEventListener('hashchange', this.handleHashChange);
    window.removeEventListener('popstate', this.handlePopState);
    this.routes.clear();
    this.currentRoute = null;
    this.previousRoute = null;
  }
}

// Singleton instance
const router = new Router();

// Export both the instance and the class
export { Router };
export default router;

/**
 * Common route guards
 */
export const guards = {
  /**
   * Require authentication
   */
  requireAuth({ route }) {
    // Check if user is authenticated
    // For now, always allow (implement auth later)
    return true;
  },

  /**
   * Require specific permission
   */
  requirePermission(permission) {
    return ({ route }) => {
      // Check if user has permission
      // For now, always allow (implement RBAC later)
      return true;
    };
  },

  /**
   * Require project selected
   */
  requireProject({ route, params }) {
    const projectId = localStorage.getItem('dss_selected_project');

    if (!projectId) {
      notifyError('Please select a project first', ErrorCode.USER_ACTION_FORBIDDEN);
      return false;
    }

    return true;
  },
};
