import assert from "node:assert/strict";
import { mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";

import { getPersistedLanAuthPath, normalizeLanToken, resolveLanAuthConfig } from "../src/lan-auth.js";

const envToken = "env-shared-secret-123";
const envAuth = resolveLanAuthConfig(tempDir(), { OPENPETS_LAN_TOKEN: envToken }, { serverMode: true });
assert.equal(envAuth.token, envToken, "valid environment token should be used directly");
assert.equal(envAuth.source, "env");
assert.equal(envAuth.insecure, false);
assert.equal(envAuth.tokenHint, "-123");

const serverDir = tempDir();
const generatedAuth = resolveLanAuthConfig(serverDir, {}, { serverMode: true });
assert.equal(generatedAuth.source, "generated", "server mode should generate a token when none exists");
assert.equal(generatedAuth.insecure, false);
assert.ok(generatedAuth.token && generatedAuth.token.length >= 32, "generated token should be long enough for LAN auth");

const persisted = JSON.parse(readFileSync(getPersistedLanAuthPath(serverDir), "utf8")) as { token: string };
assert.equal(persisted.token, generatedAuth.token, "generated token should be written to disk");
if (process.platform !== "win32") {
  assert.equal(statSync(getPersistedLanAuthPath(serverDir)).mode & 0o077, 0, "persisted token should not be readable by group/other users");
}

const reusedAuth = resolveLanAuthConfig(serverDir, {}, { serverMode: true });
assert.equal(reusedAuth.source, "stored", "existing persisted token should be reused");
assert.equal(reusedAuth.token, generatedAuth.token);

const statusOnlyDir = tempDir();
const statusOnlyAuth = resolveLanAuthConfig(statusOnlyDir, {}, { serverMode: true, generateIfMissing: false });
assert.equal(statusOnlyAuth.token, null, "read-only status auth checks should not generate a new token");
assert.equal(statusOnlyAuth.source, "none");

const clientDir = tempDir();
const clientAuth = resolveLanAuthConfig(clientDir, {}, { serverMode: false });
assert.equal(clientAuth.token, null, "client-only mode should not generate a mismatched token");
assert.equal(clientAuth.source, "none");
assert.equal(clientAuth.insecure, false);

const staleClientDir = tempDir();
writeFileSync(getPersistedLanAuthPath(staleClientDir), JSON.stringify({ version: 1, token: "stored-server-token-123", updatedAt: Date.now() }));
const staleClientAuth = resolveLanAuthConfig(staleClientDir, {}, { serverMode: false });
assert.equal(staleClientAuth.token, null, "client mode should not reuse a token generated by a previous server run");
assert.equal(staleClientAuth.source, "none");

const insecureAuth = resolveLanAuthConfig(tempDir(), { OPENPETS_LAN_ALLOW_INSECURE: "1" }, { serverMode: true });
assert.equal(insecureAuth.token, null, "insecure mode should intentionally run without a token");
assert.equal(insecureAuth.source, "none");
assert.equal(insecureAuth.insecure, true);

assert.equal(normalizeLanToken("short"), null, "short tokens should be rejected");
assert.equal(normalizeLanToken("  long-enough-token  "), "long-enough-token", "tokens should be trimmed");

rmSync(serverDir, { recursive: true, force: true });
rmSync(statusOnlyDir, { recursive: true, force: true });
rmSync(clientDir, { recursive: true, force: true });
rmSync(staleClientDir, { recursive: true, force: true });

console.log("LAN auth validation passed.");

function tempDir(): string {
  return mkdtempSync(join(tmpdir(), "openpets-lan-auth-"));
}
