import { afterEach, beforeEach, describe, expect, test } from "bun:test";
import { mkdtempSync, rmSync, writeFileSync, chmodSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { loadConfig, defaultConfig } from "../src/config";
import { CollectorFatalError } from "../src/errors";

let dir: string;
const originalEnv = process.env.OVERDECK_CONFIG_DIR;

beforeEach(() => {
  dir = mkdtempSync(join(tmpdir(), "overdeck-config-"));
  process.env.OVERDECK_CONFIG_DIR = dir;
});

afterEach(() => {
  rmSync(dir, { recursive: true, force: true });
  if (originalEnv === undefined) delete process.env.OVERDECK_CONFIG_DIR;
  else process.env.OVERDECK_CONFIG_DIR = originalEnv;
});

describe("config fail-closed startup", () => {
  test("missing config file falls back to defaults", () => {
    const config = loadConfig();
    expect(config).toEqual(defaultConfig());
  });

  test("valid config.toml is parsed", () => {
    writeFileSync(
      join(dir, "config.toml"),
      `port = 5000\ntailnetBind = true\nbindHost = "100.64.0.1"\n`,
    );
    const config = loadConfig();
    expect(config.port).toBe(5000);
    expect(config.tailnetBind).toBe(true);
    expect(config.bindHost).toBe("100.64.0.1");
    expect(config.bind_port).toBe(5000);
  });

  test("bind_port defaults to port and an explicit value wins", () => {
    writeFileSync(join(dir, "config.toml"), `port = 31338\n`);
    expect(loadConfig().bind_port).toBe(31338);

    writeFileSync(join(dir, "config.toml"), `port = 31338\nbind_port = 31339\n`);
    expect(loadConfig().bind_port).toBe(31339);
  });

  test("invalid TOML syntax throws a named fatal error", () => {
    writeFileSync(join(dir, "config.toml"), `port = "unterminated`);
    expect(() => loadConfig()).toThrow(CollectorFatalError);
    try {
      loadConfig();
    } catch (err) {
      expect((err as CollectorFatalError).code).toBe("CONFIG_INVALID_TOML");
    }
  });

  test("valid TOML but wrong shape throws a named fatal error", () => {
    writeFileSync(join(dir, "config.toml"), `port = "not-a-number"`);
    try {
      loadConfig();
      throw new Error("expected loadConfig to throw");
    } catch (err) {
      expect(err).toBeInstanceOf(CollectorFatalError);
      expect((err as CollectorFatalError).code).toBe("CONFIG_INVALID_SHAPE");
    }
  });

  test("unreadable config file throws a named fatal error", () => {
    const path = join(dir, "config.toml");
    writeFileSync(path, `port = 5000`);
    chmodSync(path, 0o000);
    try {
      expect(() => loadConfig()).toThrow(CollectorFatalError);
    } finally {
      chmodSync(path, 0o600);
    }
  });
});
