import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'fs';
import { dirname, join, resolve, sep } from 'path';
import { PlatformError } from '../../kernel/errors.mjs';
import { validateModulePackage } from './module-package-contract.mjs';

function ensureInside(root, pathname) {
  const base = resolve(root);
  const target = resolve(pathname);
  if (target !== base && !target.startsWith(`${base}${sep}`)) {
    throw new PlatformError('Moduldatei liegt außerhalb des Modulspeichers.', { code: 'MODULE_STORAGE_ESCAPE', status: 400, expose: true });
  }
  return target;
}

export class ModulePackageStore {
  #root;
  #trustKeys;

  constructor(root, { trustKeys = {} } = {}) {
    this.#root = resolve(root);
    this.#trustKeys = { ...trustKeys };
    mkdirSync(this.#root, { recursive: true });
  }

  #moduleRoot(id) {
    return ensureInside(this.#root, join(this.#root, id));
  }

  #packagePath(id) {
    return join(this.#moduleRoot(id), 'package.vmod');
  }

  list() {
    if (!existsSync(this.#root)) return [];
    const result = [];
    for (const entry of readdirSync(this.#root, { withFileTypes: true })) {
      if (!entry.isDirectory()) continue;
      try {
        const packagePath = this.#packagePath(entry.name);
        if (!existsSync(packagePath)) continue;
        const parsed = JSON.parse(readFileSync(packagePath, 'utf8'));
        const pkg = validateModulePackage(parsed, { trustKeys: this.#trustKeys });
        result.push({
          id: pkg.manifest.id,
          manifest: pkg.manifest,
          runtime: pkg.runtime,
          trust: pkg.trust,
          installedAt: String(parsed.installedAt || parsed.createdAt || ''),
          activatable: pkg.runtime.mode === 'declarative' || (pkg.runtime.mode === 'isolated' && pkg.trust.trusted),
          packagePath,
        });
      } catch (error) {
        result.push({ id: entry.name, invalid: true, error: error?.message || String(error), activatable: false });
      }
    }
    return result.sort((a, b) => String(a.id).localeCompare(String(b.id)));
  }

  get(id) {
    const packagePath = this.#packagePath(id);
    if (!existsSync(packagePath)) return null;
    const parsed = JSON.parse(readFileSync(packagePath, 'utf8'));
    return validateModulePackage(parsed, { trustKeys: this.#trustKeys });
  }

  install(input) {
    const pkg = validateModulePackage(input, { trustKeys: this.#trustKeys });
    const root = this.#moduleRoot(pkg.manifest.id);
    if (existsSync(root)) throw new PlatformError('Dieses Fremdmodul ist bereits installiert.', { code: 'MODULE_ALREADY_INSTALLED', status: 409, expose: true });
    const temporary = `${root}.${process.pid}.tmp`;
    mkdirSync(temporary, { recursive: true });
    try {
      for (const file of pkg.files) {
        const destination = ensureInside(temporary, join(temporary, file.path));
        mkdirSync(dirname(destination), { recursive: true });
        writeFileSync(destination, Buffer.from(file.content, 'base64'), { mode: 0o600 });
      }
      const persisted = { ...pkg, trust: undefined, installedAt: new Date().toISOString() };
      writeFileSync(join(temporary, 'package.vmod'), `${JSON.stringify(persisted, null, 2)}\n`, { mode: 0o600 });
      renameSync(temporary, root);
    } catch (error) {
      rmSync(temporary, { recursive: true, force: true });
      throw error;
    }
    return this.describe(pkg.manifest.id);
  }

  remove(id) {
    const root = this.#moduleRoot(id);
    if (!existsSync(root)) return false;
    rmSync(root, { recursive: true, force: true });
    return true;
  }

  export(id) {
    const packagePath = this.#packagePath(id);
    if (!existsSync(packagePath)) throw new PlatformError('Installiertes Modulpaket nicht gefunden.', { code: 'MODULE_NOT_FOUND', status: 404, expose: true });
    return readFileSync(packagePath);
  }

  describe(id) {
    const pkg = this.get(id);
    if (!pkg) return null;
    return {
      id: pkg.manifest.id,
      manifest: pkg.manifest,
      runtime: pkg.runtime,
      trust: pkg.trust,
      activatable: pkg.runtime.mode === 'declarative' || (pkg.runtime.mode === 'isolated' && pkg.trust.trusted),
    };
  }
}
