import { readFile } from "node:fs/promises";

import { createControlCoreApp } from "./app.mjs";
import { resolveDeviceDatabaseUrl } from "./database-config.mjs";
import { PostgresDeviceRepository } from "./postgres-repository.mjs";

const config = await readConfig();
const repository = new PostgresDeviceRepository({
  databaseUrl: config.databaseUrl,
  poolSize: config.databasePoolSize,
});

await repository.migrate();

const server = createControlCoreApp({
  repository,
  gatewayToken: config.gatewayToken,
  identifierPepper: config.identifierPepper,
  discoveryIngestEnabled: config.discoveryIngestEnabled,
});

server.listen(config.port, config.host, () => {
  console.log(JSON.stringify({
    event: "device_control_core_started",
    host: config.host,
    port: config.port,
    discoveryIngest: config.discoveryIngestEnabled,
    commandTransport: "disabled",
  }));
});

process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);

async function shutdown() {
  server.close(async () => {
    await repository.close();
    process.exit(0);
  });
}

async function readConfig() {
  const discoveryIngestEnabled = parseBoolean(
    process.env.DEVICE_DISCOVERY_INGEST_ENABLED,
    false,
  );
  return {
    host: String(process.env.HOST || "127.0.0.1").trim(),
    port: parsePort(process.env.PORT, 18120),
    databaseUrl: await resolveDeviceDatabaseUrl(process.env),
    databasePoolSize: parsePositiveInt(
      process.env.DEVICE_DATABASE_POOL_SIZE,
      10,
    ),
    discoveryIngestEnabled,
    gatewayToken: discoveryIngestEnabled
      ? await readRequiredSecretFile(
          process.env.DEVICE_GATEWAY_CORE_TOKEN_FILE,
          "device_gateway_core_token_file_required",
        )
      : "",
    identifierPepper: discoveryIngestEnabled
      ? await readRequiredSecretFile(
          process.env.DEVICE_IDENTIFIER_PEPPER_FILE,
          "device_identifier_pepper_file_required",
        )
      : "",
  };
}

async function readRequiredSecretFile(path, errorCode) {
  const normalized = requiredValue(path, errorCode);
  const value = (await readFile(normalized, "utf8")).trim();
  if (value.length < 32) throw new Error(errorCode);
  return value;
}

function requiredValue(value, errorCode) {
  if (typeof value !== "string" || value.trim() === "") {
    throw new Error(errorCode);
  }
  return value.trim();
}

function parsePort(value, fallback) {
  const parsed = Number(value || fallback);
  if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 65535) {
    throw new Error("device_control_port_invalid");
  }
  return parsed;
}

function parsePositiveInt(value, fallback) {
  const parsed = Number(value || fallback);
  if (!Number.isSafeInteger(parsed) || parsed < 1) {
    throw new Error("device_positive_integer_invalid");
  }
  return parsed;
}

function parseBoolean(value, fallback) {
  if (value === undefined || value === null || value === "") return fallback;
  const normalized = String(value).trim().toLowerCase();
  if (["1", "true", "yes", "on"].includes(normalized)) return true;
  if (["0", "false", "no", "off"].includes(normalized)) return false;
  throw new Error("device_boolean_invalid");
}
