import { describe, it, expect, vi } from 'vitest';
import { resolveServerEnv } from '../src/services/env-resolver.js';
import type { ISecretRepository } from '../src/repositories/interfaces.js';
import type { McpServer } from '@prisma/client';

function makeServer(env: unknown[]): McpServer {
  return {
    id: 'srv-1',
    name: 'test-server',
    description: '',
    packageName: null,
    dockerImage: 'test:latest',
    transport: 'STDIO',
    repositoryUrl: null,
    externalUrl: null,
    command: null,
    containerPort: null,
    replicas: 1,
    env,
    version: 1,
    createdAt: new Date(),
    updatedAt: new Date(),
  } as McpServer;
}

function mockSecretRepo(secrets: Record<string, Record<string, string>>): ISecretRepository {
  return {
    findAll: vi.fn(async () => []),
    findById: vi.fn(async () => null),
    findByName: vi.fn(async (name: string) => {
      const data = secrets[name];
      if (!data) return null;
      return { id: `sec-${name}`, name, data, version: 1, createdAt: new Date(), updatedAt: new Date() };
    }),
    create: vi.fn(async () => ({} as never)),
    update: vi.fn(async () => ({} as never)),
    delete: vi.fn(async () => {}),
  };
}

describe('resolveServerEnv', () => {
  it('resolves inline values', async () => {
    const server = makeServer([
      { name: 'FOO', value: 'bar' },
      { name: 'BAZ', value: 'qux' },
    ]);
    const repo = mockSecretRepo({});
    const result = await resolveServerEnv(server, repo);
    expect(result).toEqual({ FOO: 'bar', BAZ: 'qux' });
  });

  it('resolves secret references', async () => {
    const server = makeServer([
      { name: 'TOKEN', valueFrom: { secretRef: { name: 'ha-creds', key: 'HOMEASSISTANT_TOKEN' } } },
    ]);
    const repo = mockSecretRepo({
      'ha-creds': { HOMEASSISTANT_TOKEN: 'secret-token-123' },
    });
    const result = await resolveServerEnv(server, repo);
    expect(result).toEqual({ TOKEN: 'secret-token-123' });
  });

  it('handles mixed inline and secret refs', async () => {
    const server = makeServer([
      { name: 'URL', value: 'https://ha.local' },
      { name: 'TOKEN', valueFrom: { secretRef: { name: 'creds', key: 'TOKEN' } } },
    ]);
    const repo = mockSecretRepo({
      creds: { TOKEN: 'my-token' },
    });
    const result = await resolveServerEnv(server, repo);
    expect(result).toEqual({ URL: 'https://ha.local', TOKEN: 'my-token' });
  });

  it('caches secret lookups', async () => {
    const server = makeServer([
      { name: 'A', valueFrom: { secretRef: { name: 'shared', key: 'KEY_A' } } },
      { name: 'B', valueFrom: { secretRef: { name: 'shared', key: 'KEY_B' } } },
    ]);
    const repo = mockSecretRepo({
      shared: { KEY_A: 'val-a', KEY_B: 'val-b' },
    });
    const result = await resolveServerEnv(server, repo);
    expect(result).toEqual({ A: 'val-a', B: 'val-b' });
    expect(repo.findByName).toHaveBeenCalledTimes(1);
  });

  it('throws when secret not found', async () => {
    const server = makeServer([
      { name: 'TOKEN', valueFrom: { secretRef: { name: 'missing', key: 'TOKEN' } } },
    ]);
    const repo = mockSecretRepo({});
    await expect(resolveServerEnv(server, repo)).rejects.toThrow("Secret 'missing' not found");
  });

  it('throws when secret key not found', async () => {
    const server = makeServer([
      { name: 'TOKEN', valueFrom: { secretRef: { name: 'creds', key: 'NONEXISTENT' } } },
    ]);
    const repo = mockSecretRepo({
      creds: { OTHER_KEY: 'val' },
    });
    await expect(resolveServerEnv(server, repo)).rejects.toThrow("Key 'NONEXISTENT' not found in secret 'creds'");
  });

  it('returns empty map for empty env', async () => {
    const server = makeServer([]);
    const repo = mockSecretRepo({});
    const result = await resolveServerEnv(server, repo);
    expect(result).toEqual({});
  });
});
