import { describe, it, expect, beforeEach } from 'vitest';
import { ProviderCredentialService } from './provider-credential.service';

const KEY_B64 = Buffer.alloc(32, 7).toString('base64');

/** Minimal in-memory stand-in for prisma.iiosProviderCredential (upsert/findUnique). */
function fakePrisma() {
  const rows = new Map<string, Record<string, unknown>>();
  const k = (scopeId: string, providerType: string) => `${scopeId}::${providerType}`;
  return {
    _rows: rows,
    iiosProviderCredential: {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      async upsert({ where, create, update }: any) {
        const key = k(where.scopeId_providerType.scopeId, where.scopeId_providerType.providerType);
        const existing = rows.get(key);
        const row = existing ? { ...existing, ...update } : { ...create };
        rows.set(key, row);
        return row;
      },
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      async findUnique({ where }: any) {
        return rows.get(k(where.scopeId_providerType.scopeId, where.scopeId_providerType.providerType)) ?? null;
      },
    },
  };
}

function make(prisma: ReturnType<typeof fakePrisma>, env: NodeJS.ProcessEnv): ProviderCredentialService {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const s = new ProviderCredentialService(prisma as any);
  s.env = env;
  return s;
}

describe('ProviderCredentialService', () => {
  let prisma: ReturnType<typeof fakePrisma>;
  let svc: ProviderCredentialService;

  beforeEach(() => {
    prisma = fakePrisma();
    svc = make(prisma, { IIOS_CRED_KEY: KEY_B64 } as NodeJS.ProcessEnv);
  });

  it('seals the secret at rest — plaintext token never stored', async () => {
    await svc.upsert('scope_1', 'TWILIO_SMS', { accountSid: 'AC123', authToken: 'tok_secret', fromNumber: '+15550001111' });
    const stored = JSON.stringify([...prisma._rows.values()]);
    expect(stored).not.toContain('tok_secret');
    expect(stored).toContain('cipherText');
  });

  it('resolves back the exact config it sealed', async () => {
    const cfg = { accountSid: 'AC123', authToken: 'tok_secret', fromNumber: '+15550001111' };
    await svc.upsert('scope_1', 'TWILIO_SMS', cfg);
    expect(await svc.resolve('scope_1', 'TWILIO_SMS')).toEqual(cfg);
  });

  it('status is masked — reveals hints, never the token', async () => {
    await svc.upsert('scope_1', 'TWILIO_SMS', { accountSid: 'AC1234567', authToken: 'tok_secret', fromNumber: '+15550001111' });
    const status = await svc.status('scope_1', 'TWILIO_SMS');
    expect(status).toMatchObject({ configured: true, enabled: true, hints: { fromNumber: '+15550001111', sidLast4: '4567' } });
    expect(JSON.stringify(status)).not.toContain('tok_secret');
  });

  it('reports not-configured for an unknown scope', async () => {
    expect(await svc.status('nope', 'TWILIO_SMS')).toEqual({ configured: false });
    expect(await svc.resolve('nope', 'TWILIO_SMS')).toBeNull();
  });

  it('does not resolve a disabled credential', async () => {
    await svc.upsert('scope_1', 'TWILIO_SMS', { accountSid: 'AC123', authToken: 't', fromNumber: '+1' }, { enabled: false });
    expect(await svc.resolve('scope_1', 'TWILIO_SMS')).toBeNull();
  });

  it('throws when the platform key is absent (fail closed, never store plaintext)', async () => {
    const noKey = make(prisma, {} as NodeJS.ProcessEnv);
    await expect(noKey.upsert('s', 'TWILIO_SMS', { accountSid: 'A', authToken: 't', fromNumber: '+1' })).rejects.toThrow(/IIOS_CRED_KEY/);
  });
});
