/* ============================================================
 * Copyright (c) 2026 RAVAPI Soluções. www.ravapi.com
 * RecibosModule — mesma base de código em dois modos de deploy
 *
 *   Modo SaaS      — app próprio, banco próprio, gestão de usuários local
 *   Modo embarcado — importado pelo sistema de freelancers, tabelas no
 *                    schema `recibos` do banco do host, identidade delegada
 *
 * ATENÇÃO: camada de ligação, ainda não coberta por teste automatizado.
 * ============================================================ */

import { Module, type DynamicModule, type Provider } from '@nestjs/common';
import type { Pool } from '../db/client.ts';
import type { IdentityProvider } from '../core/identity.ts';
import { CadastrosService } from '../domain/cadastros.service.ts';
import { UsuariosService } from '../domain/usuarios.service.ts';
import { LicencaService } from '../domain/licenca.service.ts';
import { RecibosService } from '../domain/recibos.service.ts';
import { VerificacaoService } from '../domain/verificacao.service.ts';
import { RateLimitService } from '../domain/rate-limit.service.ts';
import { AssetsService } from '../domain/assets.service.ts';
import type { StorageAdapter } from '../domain/storage.ts';
import { AuthService } from '../domain/auth.service.ts';

export const POOL = Symbol('RECIBOS_POOL');
export const IDENTIDADE = Symbol('RECIBOS_IDENTIDADE');
export const CONFIG = Symbol('RECIBOS_CONFIG');
export const STORAGE = Symbol('RECIBOS_STORAGE');
export const SEGREDO_SESSAO = Symbol('RECIBOS_SEGREDO_SESSAO');

export interface RecibosConfig {
  /** Conexão do host ou própria. O papel NÃO pode ser superusuário: RLS não vale para superusuário. */
  pool: Pool;
  schema?: string;
  modo: 'saas' | 'embarcado';
  identidade: IdentityProvider;
  /** URL base da página pública de verificação, ex.: https://ravapi.com/v */
  urlVerificacaoPublica: string;
  /**
   * Bucket S3-compatível para logo/assinatura (docs/decisoes.md §16).
   * Sem isto, a página pública funciona normalmente — só sai sem logo.
   */
  storage?: StorageAdapter;
  /**
   * Segredo do token de sessão do login local (docs/decisoes.md §18).
   * Só faz sentido no modo SaaS — no modo embarcado a identidade já vem
   * pronta do host. Sem isto, AuthService não é provido (fica null).
   */
  segredoSessao?: string;
}

@Module({})
export class RecibosModule {
  static forRoot(config: RecibosConfig): DynamicModule {
    const provedores: Provider[] = [
      { provide: POOL, useValue: config.pool },
      { provide: IDENTIDADE, useValue: config.identidade },
      { provide: CONFIG, useValue: { ...config, schema: config.schema ?? 'recibos' } },
      { provide: STORAGE, useValue: config.storage ?? null },
      { provide: SEGREDO_SESSAO, useValue: config.segredoSessao ?? null },

      {
        provide: CadastrosService,
        useFactory: (p: Pool, l: LicencaService) =>
          new CadastrosService(p, async () => (await l.atual()).limites.empresas ?? Number.MAX_SAFE_INTEGER),
        inject: [POOL, LicencaService],
      },
      { provide: LicencaService, useFactory: (p: Pool) => new LicencaService(p), inject: [POOL] },
      {
        provide: UsuariosService,
        useFactory: (p: Pool, l: LicencaService) =>
          // O limite de usuários vem da licença do Klavo, não de constante no
          // código: mudar de plano não pode exigir deploy. Sem limite declarado,
          // não se inventa um — o plano é que manda.
          new UsuariosService(p, async () => (await l.atual()).limites.usuarios ?? Number.MAX_SAFE_INTEGER),
        inject: [POOL, LicencaService],
      },
      {
        provide: RecibosService,
        useFactory: (p: Pool, l: LicencaService) => new RecibosService(p, l),
        inject: [POOL, LicencaService],
      },
      { provide: RateLimitService, useFactory: (p: Pool) => new RateLimitService(p), inject: [POOL] },
      {
        provide: VerificacaoService,
        useFactory: (p: Pool, r: RateLimitService, s: StorageAdapter | null) =>
          new VerificacaoService(p, r, s ?? undefined),
        inject: [POOL, RateLimitService, STORAGE],
      },
      {
        provide: AssetsService,
        useFactory: (p: Pool, s: StorageAdapter | null) => {
          if (!s) return null;
          return new AssetsService(p, s);
        },
        inject: [POOL, STORAGE],
      },
      {
        provide: AuthService,
        useFactory: (p: Pool, segredo: string | null) => (segredo ? new AuthService(p, segredo) : null),
        inject: [POOL, SEGREDO_SESSAO],
      },
    ];

    return {
      module: RecibosModule,
      providers: provedores,
      exports: [
        CadastrosService, UsuariosService, LicencaService, RecibosService,
        VerificacaoService, RateLimitService, AssetsService, AuthService,
        STORAGE, SEGREDO_SESSAO, IDENTIDADE, CONFIG,
      ],
    };
  }
}
