'use strict';

const { HOOK_EVENTS, normalizeHookEvent } = require('./manifest');

class HookRuntime {
  constructor({ logger = console } = {}) {
    this.logger = logger;
    this.handlers = new Map();
    this.packages = new Map();
    this.diagnostics = [];
  }

  registerPackage(hookPackage) {
    const manifest = hookPackage?.manifest;
    if (!manifest?.id) throw new Error('Hook package manifest id is required');
    if (this.packages.has(manifest.id)) throw new Error(`Duplicate hook package: ${manifest.id}`);
    const moduleExports = hookPackage.module || require(hookPackage.entryPath);
    const registered = {
      ...hookPackage,
      module: moduleExports,
    };
    this.packages.set(manifest.id, registered);
    for (const rawEvent of manifest.events || []) {
      const event = normalizeHookEvent(rawEvent);
      if (!HOOK_EVENTS.includes(event)) continue;
      const list = this.handlers.get(event) || [];
      list.push({ id: manifest.id, event, module: moduleExports, permissions: manifest.permissions || {} });
      this.handlers.set(event, list);
    }
    return registered;
  }

  registerDiscovered(discoveryResult = {}) {
    for (const diagnostic of discoveryResult.diagnostics || []) this.diagnostics.push(diagnostic);
    for (const hookPackage of discoveryResult.packages || []) {
      try {
        this.registerPackage(hookPackage);
      } catch (err) {
        this.diagnostics.push({ level: 'error', id: hookPackage.manifest?.id, reason: err.message });
      }
    }
  }

  async trigger(event, payload = {}, context = {}) {
    const normalizedEvent = normalizeHookEvent(event);
    if (!HOOK_EVENTS.includes(normalizedEvent)) throw new Error(`Unsupported hook event: ${event}`);
    let current = payload;
    const handlers = this.handlers.get(normalizedEvent) || [];
    for (const handler of handlers) {
      try {
        const next = await callHandler(handler, normalizedEvent, current, context);
        if (next !== undefined) current = next;
      } catch (err) {
        const diagnostic = { level: 'error', hook: handler.id, event, reason: err.message };
        this.diagnostics.push(diagnostic);
        if (this.logger?.error) this.logger.error(`[hook-runtime] ${handler.id}:${event}: ${err.message}`);
      }
    }
    return current;
  }

  listHooks() {
    return [...this.packages.values()].map(pkg => ({
      id: pkg.manifest.id,
      version: pkg.manifest.version,
      events: pkg.manifest.events,
      permissions: pkg.manifest.permissions,
    }));
  }

  getDiagnostics() {
    return [...this.diagnostics];
  }
}

async function callHandler(handler, event, payload, context) {
  const exported = handler.module;
  if (typeof exported === 'function') return exported(event, payload, context);
  if (typeof exported?.handle === 'function') return exported.handle(event, payload, context);
  if (typeof exported?.[event] === 'function') return exported[event](payload, context);
  return undefined;
}

module.exports = {
  HookRuntime,
};
