import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { PrismaPg } from '@prisma/adapter-pg';
import pg from 'pg';

// ADR 0006 revogado: BD-por-workspace → schema `sar` no ERP.
// Pool keyed por idEmpresa (number). URL deve incluir ?schema=sar.
// CODING-RULES PGD-DB-009: callers obtêm o client via CLS, não injetando este serviço.

const MAX_ENTRIES = 10; // LRU cap; ajustável via env na próxima iteração
const PG_POOL_SIZE = 5;
const noop = (): void => undefined;

interface PoolEntry {
  client: PrismaClient;
  pgPool: pg.Pool;
}

@Injectable()
export class WorkspacePrismaPool implements OnModuleDestroy {
  private readonly logger = new Logger(WorkspacePrismaPool.name);

  // Map preserves insertion order → LRU: primeiro = mais antigo, último = mais recente
  private readonly cache = new Map<string, PoolEntry>();

  getOrCreate(idEmpresa: number, dbUrl: string): PrismaClient {
    const key = String(idEmpresa);
    const hit = this.cache.get(key);
    if (hit) {
      // Move para o fim (LRU refresh)
      this.cache.delete(key);
      this.cache.set(key, hit);
      return hit.client;
    }

    if (this.cache.size >= MAX_ENTRIES) {
      this.evictOldest();
    }

    const pgPool = new pg.Pool({
      connectionString: dbUrl,
      max: PG_POOL_SIZE,
      options: '-c search_path=sar',
    });
    const adapter = new PrismaPg(pgPool, { schema: 'sar' });
    const client = new PrismaClient({ adapter });
    this.cache.set(key, { client, pgPool });
    this.logger.log(`pool criado: idEmpresa=${idEmpresa} total=${this.cache.size}`);
    return client;
  }

  async health(k = 3): Promise<{ workspaceId: string; ok: boolean; latencyMs?: number }[]> {
    // Verifica os k empresas mais recentes
    const entries = [...this.cache.entries()].slice(-k);
    return Promise.all(
      entries.map(async ([workspaceId, { pgPool }]) => {
        const start = Date.now();
        try {
          const conn = await pgPool.connect();
          conn.release();
          return { workspaceId, ok: true, latencyMs: Date.now() - start };
        } catch {
          return { workspaceId, ok: false };
        }
      }),
    );
  }

  async onModuleDestroy(): Promise<void> {
    await Promise.allSettled(
      [...this.cache.values()].map(({ client, pgPool }) =>
        client.$disconnect().finally(() => pgPool.end()),
      ),
    );
    this.cache.clear();
    this.logger.log('pool destruído — todos os clientes desconectados');
  }

  private evictOldest(): void {
    const [oldestId, oldest] = this.cache.entries().next().value as [string, PoolEntry];
    void oldest.client.$disconnect().catch(noop);
    void oldest.pgPool.end().catch(noop);
    this.cache.delete(oldestId);
    this.logger.log(`evicted LRU idEmpresa=${oldestId}`);
  }
}
