import path from "node:path";
import { fileURLToPath } from "node:url";
import { config as loadDotenv } from "dotenv";
import { z } from "zod";

loadDotenv({ quiet: true });
loadDotenv({ path: path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../.env"), quiet: true });

const OptionalUrlSchema = z.preprocess(
  (value) => (typeof value === "string" && value.trim() === "" ? undefined : value),
  z.string().trim().url().optional()
);

const DEFAULT_HERMES_AGENT_API_BASE_URL = "http://127.0.0.1:8642/v1";

const HermesAgentApiBaseUrlSchema = z.preprocess(
  (value) => (typeof value === "string" && value.trim() === "" ? undefined : value),
  z.string().trim().url().default(DEFAULT_HERMES_AGENT_API_BASE_URL)
);

const ChatWebSearchEngineSchema = z.preprocess(
  (value) => {
    if (typeof value !== "string") return value;
    const trimmed = value.trim();
    return trimmed ? trimmed.toLowerCase() : undefined;
  },
  z.enum(["exa", "searxng", "brave"]).default("exa")
);

const BooleanFlagSchema = z.preprocess((value) => {
  if (typeof value !== "string") return value;
  const normalized = value.trim().toLowerCase();
  if (!normalized) return undefined;
  if (["1", "true", "yes", "on"].includes(normalized)) return true;
  if (["0", "false", "no", "off"].includes(normalized)) return false;
  return value;
}, z.boolean().default(false));

const OptionalTrimmedStringSchema = z.preprocess(
  (value) => (typeof value === "string" && value.trim() === "" ? undefined : value),
  z.string().trim().min(1).optional()
);

function defaultedPositiveInt(defaultValue: number) {
  return z.preprocess(
    (value) => (typeof value === "string" && value.trim() === "" ? undefined : value),
    z.coerce.number().int().positive().default(defaultValue)
  );
}

function defaultedTrimmedString(defaultValue: string) {
  return z.preprocess(
    (value) => (typeof value === "string" && value.trim() === "" ? undefined : value),
    z.string().trim().min(1).default(defaultValue)
  );
}

const EnvSchema = z.object({
  PORT: z.coerce.number().int().positive().default(8787),
  HOST: z.string().default("0.0.0.0"),

  // simple bearer-token auth for your personal backend
  ADMIN_TOKEN: z.string().min(20).optional(),

  // provider keys
  OPENAI_API_KEY: z.string().optional(),
  ANTHROPIC_API_KEY: z.string().optional(),
  XAI_API_KEY: z.string().optional(),
  GEMINI_API_KEY: z.string().optional(),
  HERMES_AGENT_API_BASE_URL: HermesAgentApiBaseUrlSchema,
  HERMES_AGENT_API_KEY: OptionalTrimmedStringSchema,
  HERMES_AGENT_MODEL: OptionalTrimmedStringSchema,
  EXA_API_KEY: z.string().optional(),
  BRAVE_SEARCH_API_KEY: OptionalTrimmedStringSchema,

  // Chat-mode web_search tool configuration. Search mode remains Exa-only for now.
  CHAT_WEB_SEARCH_ENGINE: ChatWebSearchEngineSchema,
  SEARXNG_BASE_URL: OptionalUrlSchema,
  CHAT_MAX_TOOL_ROUNDS: defaultedPositiveInt(100),

  // Optional chat-mode Codex tool. When enabled, the server SSHes into a remote
  // devbox and runs `codex exec` in a persistent scratch directory there.
  CHAT_CODEX_TOOL_ENABLED: BooleanFlagSchema,
  CHAT_CODEX_REMOTE_HOST: OptionalTrimmedStringSchema,
  CHAT_CODEX_REMOTE_USER: OptionalTrimmedStringSchema,
  CHAT_CODEX_REMOTE_PORT: defaultedPositiveInt(22),
  CHAT_CODEX_REMOTE_WORKDIR: defaultedTrimmedString("/workspace/sybil-codex"),
  CHAT_CODEX_SSH_KEY_PATH: OptionalTrimmedStringSchema,
  CHAT_CODEX_SSH_PRIVATE_KEY_B64: OptionalTrimmedStringSchema,
  CHAT_CODEX_EXEC_TIMEOUT_MS: defaultedPositiveInt(600_000),

  // Optional arbitrary shell tool that runs only on the configured devbox.
  CHAT_SHELL_TOOL_ENABLED: BooleanFlagSchema,
  CHAT_SHELL_EXEC_TIMEOUT_MS: defaultedPositiveInt(120_000),
}).superRefine((value, ctx) => {
  if (value.CHAT_WEB_SEARCH_ENGINE === "searxng" && !value.SEARXNG_BASE_URL) {
    ctx.addIssue({
      code: "custom",
      path: ["SEARXNG_BASE_URL"],
      message: "SEARXNG_BASE_URL is required when CHAT_WEB_SEARCH_ENGINE=searxng",
    });
  }

  if (value.CHAT_WEB_SEARCH_ENGINE === "brave" && !value.BRAVE_SEARCH_API_KEY) {
    ctx.addIssue({
      code: "custom",
      path: ["BRAVE_SEARCH_API_KEY"],
      message: "BRAVE_SEARCH_API_KEY is required when CHAT_WEB_SEARCH_ENGINE=brave",
    });
  }

  if ((value.CHAT_CODEX_TOOL_ENABLED || value.CHAT_SHELL_TOOL_ENABLED) && !value.CHAT_CODEX_REMOTE_HOST) {
    ctx.addIssue({
      code: "custom",
      path: ["CHAT_CODEX_REMOTE_HOST"],
      message: "CHAT_CODEX_REMOTE_HOST is required when CHAT_CODEX_TOOL_ENABLED=true or CHAT_SHELL_TOOL_ENABLED=true",
    });
  }
});

export type Env = z.infer<typeof EnvSchema>;

export const env: Env = EnvSchema.parse(process.env);
