import { readFileSync } from 'fs';
import { PlatformError } from '../../kernel/errors.mjs';
import { buildModulePackage } from '../../core/modules/module-package-contract.mjs';

export const PROTECTED_MODULE_IDS = Object.freeze([
  'vendoo.system',
  'vendoo.security',
  'vendoo.auth',
  'vendoo.users',
  'vendoo.sessions',
  'vendoo.settings',
  'vendoo.themes',
  'vendoo.listings',
  'vendoo.inventory',
  'vendoo.media',
  'vendoo.operations',
  'vendoo.module-manager',
  'vendoo.deployments',
]);
const PROTECTED_MODULES = new Set(PROTECTED_MODULE_IDS);

function asPublicModule(record, packagesById) {
  const external = record.source === 'external';
  const pkg = packagesById.get(record.id);
  const protectedModule = PROTECTED_MODULES.has(record.id);
  const canDisable = !protectedModule && record.enabled;
  const canEnable = !record.enabled && (!external || pkg?.activatable === true);
  return {
    ...record,
    protected: protectedModule,
    removable: external && !record.enabled,
    exportable: true,
    installSource: external ? 'installed' : 'builtin',
    package: pkg ? {
      runtime: pkg.runtime,
      trust: pkg.trust,
      activatable: pkg.activatable,
      invalid: Boolean(pkg.invalid),
      error: pkg.error || null,
    } : null,
    actions: {
      enable: canEnable,
      disable: canDisable,
      remove: external && !record.enabled,
      export: true,
    },
  };
}

export function createExternalLifecycle(pkg) {
  return {
    async start() {
      if (pkg.runtime.mode === 'isolated') {
        throw new PlatformError('Der isolierte Extension-Host ist für ausführbare Fremdmodule noch nicht aktiviert.', {
          code: 'MODULE_EXTENSION_HOST_UNAVAILABLE', status: 409, expose: true,
        });
      }
    },
    async stop() {},
    async health() {
      return { ok: true, runtime: pkg.runtime.mode, external: true };
    },
  };
}

export class ModuleManagerService {
  #modules;
  #stateStore;
  #packageStore;
  #context;

  constructor({ modules, stateStore, packageStore, context }) {
    this.#modules = modules;
    this.#stateStore = stateStore;
    this.#packageStore = packageStore;
    this.#context = context;
  }

  registerInstalledPackages() {
    const installed = this.#packageStore.list();
    for (const entry of installed) {
      if (entry.invalid || this.#modules.has(entry.id)) continue;
      const pkg = this.#packageStore.get(entry.id);
      const enabled = entry.activatable && this.#stateStore.isEnabled(entry.id, false);
      this.#modules.register({ manifest: pkg.manifest, lifecycle: createExternalLifecycle(pkg), source: 'external' }, { enabled });
    }
    return installed.length;
  }

  catalog() {
    const packages = this.#packageStore.list();
    const packagesById = new Map(packages.filter(item => item.id).map(item => [item.id, item]));
    const modules = this.#modules.snapshot().map(record => asPublicModule(record, packagesById));
    for (const pkg of packages) {
      if (!pkg.invalid || modules.some(item => item.id === pkg.id)) continue;
      modules.push({
        id: pkg.id,
        name: pkg.id,
        version: null,
        type: 'extension-host',
        status: 'invalid',
        source: 'external',
        enabled: false,
        state: 'invalid',
        protected: false,
        removable: true,
        exportable: false,
        installSource: 'installed',
        package: { invalid: true, error: pkg.error, activatable: false },
        actions: { enable: false, disable: false, remove: true, export: false },
      });
    }
    return {
      modules: modules.sort((a, b) => String(a.name || a.id).localeCompare(String(b.name || b.id), 'de')),
      summary: {
        total: modules.length,
        enabled: modules.filter(item => item.enabled).length,
        disabled: modules.filter(item => !item.enabled && item.state !== 'invalid').length,
        native: modules.filter(item => item.status === 'native').length,
        legacy: modules.filter(item => item.status === 'legacy-bridge').length,
        external: modules.filter(item => item.source === 'external').length,
        protected: modules.filter(item => item.protected).length,
      },
      safety: {
        unsignedDeclarativeAllowed: true,
        executableRequiresTrustedSignature: true,
        isolatedHostAvailable: false,
        directMainProcessExecution: false,
      },
    };
  }

  async enable(id) {
    const record = this.#modules.get(id);
    if (!record) throw new PlatformError('Modul nicht gefunden.', { code: 'MODULE_NOT_FOUND', status: 404, expose: true });
    if (record.source === 'external') {
      const pkg = this.#packageStore.describe(id);
      if (!pkg?.activatable) {
        throw new PlatformError('Dieses Fremdmodul ist nicht aktivierbar. Ausführbarer Code benötigt eine vertrauenswürdige Signatur und den isolierten Extension-Host.', {
          code: 'MODULE_NOT_ACTIVATABLE', status: 409, expose: true,
        });
      }
      if (pkg.runtime.mode === 'isolated') {
        throw new PlatformError('Der isolierte Extension-Host ist noch nicht verfügbar.', { code: 'MODULE_EXTENSION_HOST_UNAVAILABLE', status: 409, expose: true });
      }
    }
    const enabling = new Set();
    const enableWithDependencies = async moduleId => {
      if (enabling.has(moduleId)) throw new PlatformError('Zyklische Modulabhängigkeit erkannt.', { code: 'MODULE_DEPENDENCY_CYCLE', status: 409, expose: true });
      const target = this.#modules.get(moduleId);
      if (!target) throw new PlatformError(`Fehlende Modulabhängigkeit: ${moduleId}`, { code: 'MODULE_DEPENDENCY_MISSING', status: 409, expose: true });
      if (target.enabled && target.state === 'running') return this.#modules.describe(moduleId);
      enabling.add(moduleId);
      for (const dependency of target.manifest.requires) await enableWithDependencies(dependency);
      const enabled = await this.#modules.enable(moduleId, this.#context);
      this.#stateStore.setEnabled(moduleId, true, { source: moduleId === id ? 'module-manager' : 'module-manager-dependency' });
      enabling.delete(moduleId);
      return enabled;
    };
    return enableWithDependencies(id);
  }

  async disable(id, { cascade = false } = {}) {
    if (PROTECTED_MODULES.has(id)) {
      throw new PlatformError('Dieses Kernmodul ist geschützt und kann nicht deaktiviert werden.', {
        code: 'MODULE_PROTECTED', status: 409, expose: true,
      });
    }
    const dependents = this.#modules.dependentsOf(id, { enabledOnly: true, recursive: true });
    const protectedDependents = dependents.filter(dependent => PROTECTED_MODULES.has(dependent));
    if (protectedDependents.length) {
      throw new PlatformError('Das Modul wird von geschützten Kernmodulen benötigt.', {
        code: 'MODULE_REQUIRED_BY_PROTECTED', status: 409, expose: true, details: { dependents: protectedDependents },
      });
    }
    const result = await this.#modules.disable(id, this.#context, { cascade: Boolean(cascade) });
    for (const disabledId of result.disabled) this.#stateStore.setEnabled(disabledId, false, { source: 'module-manager' });
    return result;
  }

  install(buffer) {
    if (!Buffer.isBuffer(buffer) || !buffer.length) throw new PlatformError('Modulpaket fehlt.', { code: 'MODULE_PACKAGE_REQUIRED', status: 400, expose: true });
    let parsed;
    try { parsed = JSON.parse(buffer.toString('utf8')); }
    catch (error) { throw new PlatformError('Das .vmod-Paket enthält kein gültiges JSON.', { code: 'MODULE_PACKAGE_JSON_INVALID', status: 400, expose: true, cause: error }); }
    const id = String(parsed?.manifest?.id || '');
    if (this.#modules.has(id)) throw new PlatformError('Eine Modul-ID mit diesem Namen ist bereits registriert.', { code: 'MODULE_DUPLICATE', status: 409, expose: true });
    const installed = this.#packageStore.install(parsed);
    const pkg = this.#packageStore.get(installed.id);
    try {
      this.#modules.register({ manifest: pkg.manifest, lifecycle: createExternalLifecycle(pkg), source: 'external' }, { enabled: false });
      this.#stateStore.setEnabled(installed.id, false, { source: 'module-install' });
    } catch (error) {
      this.#packageStore.remove(installed.id);
      this.#stateStore.remove(installed.id);
      throw error;
    }
    return this.catalog().modules.find(item => item.id === installed.id);
  }

  async remove(id) {
    const record = this.#modules.get(id);
    if (!record) {
      const removedInvalid = this.#packageStore.remove(id);
      if (removedInvalid) this.#stateStore.remove(id);
      return { ok: removedInvalid, id };
    }
    if (record.source !== 'external') {
      throw new PlatformError('Eingebaute Vendoo-Module können nicht gelöscht werden.', { code: 'MODULE_BUILTIN_NOT_REMOVABLE', status: 409, expose: true });
    }
    if (record.enabled) throw new PlatformError('Das Modul muss vor dem Löschen deaktiviert werden.', { code: 'MODULE_DISABLE_BEFORE_REMOVE', status: 409, expose: true });
    await this.#modules.unregister(id, this.#context);
    this.#packageStore.remove(id);
    this.#stateStore.remove(id);
    return { ok: true, id };
  }

  export(id) {
    const record = this.#modules.get(id);
    if (!record) throw new PlatformError('Modul nicht gefunden.', { code: 'MODULE_NOT_FOUND', status: 404, expose: true });
    if (record.source === 'external') return this.#packageStore.export(id);
    const pkg = buildModulePackage({
      manifest: record.manifest,
      runtime: { mode: 'builtin-reference', entry: null },
      files: [{ path: 'module.json', content: `${JSON.stringify(record.manifest, null, 2)}\n` }],
    });
    return Buffer.from(`${JSON.stringify(pkg, null, 2)}\n`, 'utf8');
  }

  stateSnapshot() {
    return this.#stateStore.snapshot();
  }
}
