/**
 * TenantManager — central coordination for multi-tenant NanoClaw.
 *
 * Loads tenant configs from DB, maps channels to tenants,
 * tracks per-tenant resource usage, and provides tenant-scoped secrets.
 */
import { selectCredentialForTenant } from './credential-pool.js';
import { getAllTenants, getRegisteredGroup, getTenant } from './db.js';
import { readEnvFile } from './env.js';
import { logger } from './logger.js';
import {
  getRuntimeCredentialEnvKeys,
  getTenantPiBackend,
  getTenantProvider,
} from './runtime-provider.js';
import { RegisteredGroup, Tenant } from './types.js';

export class TenantManager {
  private tenants = new Map<string, Tenant>();

  /**
   * Load all tenants from the database.
   */
  loadFromDb(): void {
    const tenants = getAllTenants();
    this.tenants.clear();
    for (const t of tenants) {
      this.tenants.set(t.id, t);
    }
    logger.info(
      { tenantCount: this.tenants.size },
      'TenantManager loaded tenants',
    );
  }

  /**
   * Reload a single tenant from DB (after update).
   */
  reloadTenant(tenantId: string): void {
    const tenant = getTenant(tenantId);
    if (tenant) {
      this.tenants.set(tenantId, tenant);
    } else {
      this.tenants.delete(tenantId);
    }
  }

  getTenant(id: string): Tenant | undefined {
    return this.tenants.get(id);
  }

  getAllTenants(): Tenant[] {
    return [...this.tenants.values()];
  }

  getActiveTenants(): Tenant[] {
    return [...this.tenants.values()].filter((t) => t.status === 'active');
  }

  /**
   * Look up which tenant owns a given channel JID.
   * Uses the registered_groups table (group.tenantId).
   */
  getTenantForGroup(group: RegisteredGroup): Tenant | undefined {
    if (!group.tenantId) return undefined;
    return this.tenants.get(group.tenantId);
  }

  /**
   * Get secrets for a tenant's container.
   * Uses the credential pool for OAuth/API key selection.
   * Returns undefined if no pool credential is available.
   */
  getSecretsForTenant(tenantId: string): { secrets: Record<string, string>; credentialId?: string } | undefined {
    const tenant = this.tenants.get(tenantId);
    const provider = getTenantProvider(tenant);

    const poolResult = selectCredentialForTenant(tenant);
    if (!poolResult) {
      logger.warn({ tenantId, provider }, 'No pool credential available for tenant');
      return undefined;
    }

    // Add any extra env vars (base URL etc.) from .env
    const piBackend = provider === 'pi' ? getTenantPiBackend(tenant) : undefined;
    const extraEnv = readEnvFile(getRuntimeCredentialEnvKeys(provider, piBackend).filter((key) => key.endsWith('_BASE_URL') || key.endsWith('_AUTH_TOKEN')));
    return { secrets: { ...extraEnv, ...poolResult.secrets }, credentialId: poolResult.credentialId };
  }

  /**
   * Check if a tenant is active (not suspended/archived).
   */
  isTenantActive(tenantId: string): boolean {
    const tenant = this.tenants.get(tenantId);
    return tenant?.status === 'active';
  }
}
