'use strict';

// PluginInstance — the registry of configured, loadable plugin copies.
//
// The SSO plugin system (see nodejs/services/plugin_registry.js) distinguishes
// **plugin types** (the .js modules under nodejs/plugins/<category>/<type>.js)
// from **plugin instances** — a configured, loadable/unloadable *copy* of a
// type. You can have several instances of the same type (e.g. two Proxmox
// endpoints with their own URLs + tokens), each on its own schedule.
//
// This table holds the *non-secret* per-instance state: which type it is, its
// schedule (cron), whether it's loaded (enabled), and its non-secret config.
// Per-instance **secrets** (the configSchema fields flagged `secret:true`,
// e.g. a Proxmox `tokenSecret` or UniFi `password`) live in OpenBao at
// `secret/plugins/<id>/conf` (see nodejs/utils/plugin_secrets.js) — never in
// the DB. The DB row's `config` JSON column holds only non-secret field values.
//
// `slug` is the discovery source name passed to DiscoveryReconciler.reconcile,
// so a discovery instance's resources are attributed to a stable, human-chosen
// name rather than its uuid. Unique, so two instances can't shadow each other
// in the resource graph's `discovery_sources`.
//
// Like Resource/AccessRequest, there is no ORM auto-timestamp hook: the route
// handler stamps created_by/on + updated_by/on explicitly on every write (see
// routes/api_plugins.js). `id` (uuid) is generated by the ORM on create.

const { Model } = require('@simpleworkjs/orm');

const STATUS = {
  OK: 'ok',
  ERROR: 'error',
  RUNNING: 'running',
};

class PluginInstance extends Model {
  static fields = {
    id: { type: 'uuid', primaryKey: true },
    // A registered plugin type slug (matches a manifest `type`). Validated
    // against the registry before a row is created.
    pluginType: { type: 'string', isRequired: true, min: 1, max: 64 },
    // The plugin's category (e.g. 'discovery'). Copied from the manifest at
    // create time so the scheduler can dispatch without re-reading the registry
    // on every run (and so a later type removal still shows what the instance was).
    category: { type: 'string', isRequired: true, default: 'discovery', min: 1, max: 64 },
    // Human label for the instance.
    name: { type: 'string', isRequired: true, min: 1, max: 120 },
    // Stable handle: discovery source name + unique constraint. Lowercase
    // alnum + hyphen/underscore to stay safe as a resource-graph slug.
    slug: { type: 'string', isRequired: true, unique: true, min: 1, max: 64 },
    // Loaded into the scheduler? `false` = unloaded (no scheduled runs).
    enabled: { type: 'boolean', default: true },
    // Cron schedule (5-field). The scheduler turns this into a BullMQ
    // repeatable JobScheduler.
    cron: { type: 'string', isRequired: true, default: '0 * * * *' },
    // Non-secret configSchema field values. Secret fields are NOT here.
    config: { type: 'json', default: {} },
    // Last-run bookkeeping, updated by the scheduler worker.
    lastRunAt: { type: 'integer' },
    lastStatus: { type: 'string' },
    lastError: { type: 'text' },
    lastLog: { type: 'text' },
    // Audit stamps (set by the route handler, not by an ORM hook).
    created_by: { type: 'string' },
    created_on: { type: 'integer' },
    updated_by: { type: 'string' },
    updated_on: { type: 'integer' },
  };

  // All instances the scheduler should run: enabled only. Loaded fresh each
  // boot / load; not cached on the model (the scheduler is the source of truth
  // for what's actually scheduled).
  static async listEnabled() {
    return this.list({ where: { enabled: true } });
  }

  // Look up by slug — used by tests + the reconciler when only a slug is known.
  static async getBySlug(slug) {
    const rows = await this.list({ where: { slug } });
    return rows[0] || null;
  }
}

module.exports = { PluginInstance, STATUS };