import * as z from "zod/v4";
import { normalizeHttpAllowedHost } from "./utils/http-hosts.js";

/**
 * Coerce a string env var to a boolean.
 * "true" / "1" / "yes" → true; everything else (including "false") → false.
 * This avoids the JS `Boolean("false") === true` footgun with z.coerce.boolean().
 */
const booleanFromEnv = z
  .union([z.boolean(), z.string(), z.undefined()])
  .transform((val) => typeof val === "string" && ["true", "1", "yes"].includes(val.toLowerCase()));

const emptyStringAsUndefined = (val: unknown): unknown => val === "" ? undefined : val;
const optionalStringFromEnv = z.preprocess(emptyStringAsUndefined, z.string().optional());
const urlFromEnv = (defaultValue: string) =>
  z.preprocess(emptyStringAsUndefined, z.string().url().default(defaultValue));

function validateAllowedHosts(rawHosts: string | undefined): string | undefined {
  if (rawHosts === undefined) return undefined;

  const hosts: string[] = [];
  const invalidHosts: string[] = [];
  for (const value of rawHosts.split(",")) {
    const hostname = normalizeHttpAllowedHost(value);
    if (!hostname) {
      invalidHosts.push(value.trim());
    } else if (!hosts.includes(hostname)) {
      hosts.push(hostname);
    }
  }

  if (invalidHosts.length > 0) {
    const quotedHosts = invalidHosts.map((host) => `"${host}"`).join(", ");
    throw new Error(`Invalid HARNESS_MCP_ALLOWED_HOSTS entries: ${quotedHosts}`);
  }

  return hosts.join(",");
}

/**
 * Extract the account ID from a Harness PAT token.
 * PAT format: pat.<accountId>.<tokenId>.<secret>
 * Returns undefined if the token doesn't match the expected format.
 */
export function extractAccountIdFromToken(apiKey: string): string | undefined {
  const parts = apiKey.split(".");
  const accountId = parts[1];
  if (parts.length >= 3 && parts[0] === "pat" && accountId && accountId.length > 0) {
    return accountId;
  }
  return undefined;
}

const RawConfigSchema = z.object({
  HARNESS_MCP_MODE: z.preprocess(
    emptyStringAsUndefined,
    z.enum(["single-user", "multi-user"]).default("single-user"),
  ),
  HARNESS_API_KEY: optionalStringFromEnv,
  HARNESS_ACCOUNT_ID: optionalStringFromEnv,
  HARNESS_BASE_URL: urlFromEnv("https://app.harness.io"),
  // New names (preferred)
  HARNESS_ORG: optionalStringFromEnv,
  HARNESS_PROJECT: optionalStringFromEnv,
  // Deprecated names (backward compat)
  HARNESS_DEFAULT_ORG_ID: optionalStringFromEnv,
  HARNESS_DEFAULT_PROJECT_ID: optionalStringFromEnv,
  HARNESS_API_TIMEOUT_MS: z.coerce.number().default(30000),
  HARNESS_MAX_RETRIES: z.coerce.number().default(3),
  LOG_LEVEL: z.preprocess(
    (val) => (val === "" ? undefined : val),
    z.enum(["debug", "info", "warn", "error"]).default("info"),
  ),
  HARNESS_TOOLSETS: optionalStringFromEnv,
  HARNESS_MAX_BODY_SIZE_MB: z.coerce.number().default(10),
  HARNESS_RATE_LIMIT_RPS: z.coerce.number().default(10),
  HARNESS_READ_ONLY: booleanFromEnv.default(false),
  HARNESS_SKIP_ELICITATION: booleanFromEnv.default(false),
  HARNESS_AUTO_APPROVE_RISK: z.preprocess(
    emptyStringAsUndefined,
    z.enum(["none", "low_write", "medium_write", "high_write", "all"]).optional(),
  ),
  HARNESS_ALLOW_HTTP: booleanFromEnv.default(false),
  HARNESS_MCP_ALLOWED_HOSTS: optionalStringFromEnv.transform(validateAllowedHosts),
  HARNESS_MCP_AUTH_TOKEN: optionalStringFromEnv,
  HARNESS_MCP_ALLOW_UNAUTHENTICATED_HTTP: booleanFromEnv.default(false),
  HARNESS_FME_BASE_URL: urlFromEnv("https://api.split.io"),
  HARNESS_LOG_UNSAFE_BODIES: booleanFromEnv.default(false),
  HARNESS_PIPELINE_VERSION: z.enum(["0", "1"]).optional(),
  HARNESS_AUDIT_FILE: optionalStringFromEnv,
  HARNESS_AUDIT_WEBHOOK_URL: z.preprocess(emptyStringAsUndefined, z.string().url().optional()),
  HARNESS_AUDIT_WEBHOOK_TOKEN: optionalStringFromEnv,
  HARNESS_AUDIT_WEBHOOK_BATCH_SIZE: z.preprocess(emptyStringAsUndefined, z.coerce.number().min(1).default(10)),
  HARNESS_AUDIT_WEBHOOK_FLUSH_MS: z.preprocess(emptyStringAsUndefined, z.coerce.number().min(1).default(5000)),
});

export const ConfigSchema = RawConfigSchema.transform((data) => {
  const isMultiUser = data.HARNESS_MCP_MODE === "multi-user";

  if (isMultiUser && data.HARNESS_API_KEY) {
    throw new Error(
      "HARNESS_API_KEY must not be set in multi-user mode. " +
      "Each session must provide its own API key via the x-harness-api-key header.",
    );
  }

  if (!isMultiUser && !data.HARNESS_API_KEY) {
    throw new Error(
      "HARNESS_API_KEY is required in single-user mode.",
    );
  }

  let accountId: string | undefined;
  if (isMultiUser) {
    accountId = data.HARNESS_ACCOUNT_ID ?? "";
  } else {
    accountId = data.HARNESS_ACCOUNT_ID ?? extractAccountIdFromToken(data.HARNESS_API_KEY!);
    if (!accountId) {
      throw new Error(
        "HARNESS_ACCOUNT_ID is required when the API key is not a PAT (pat.<accountId>.<tokenId>.<secret>)",
      );
    }
  }

  if (!data.HARNESS_BASE_URL.startsWith("https://") && !data.HARNESS_ALLOW_HTTP) {
    throw new Error(
      `HARNESS_BASE_URL must use HTTPS (got "${data.HARNESS_BASE_URL}"). ` +
      "If you need HTTP for local development, set HARNESS_ALLOW_HTTP=true.",
    );
  }

  if (data.HARNESS_FME_BASE_URL && !data.HARNESS_FME_BASE_URL.startsWith("https://") && !data.HARNESS_ALLOW_HTTP) {
    throw new Error(
      `HARNESS_FME_BASE_URL must use HTTPS (got "${data.HARNESS_FME_BASE_URL}"). ` +
      "If you need HTTP for local development, set HARNESS_ALLOW_HTTP=true.",
    );
  }

  if (data.HARNESS_AUDIT_WEBHOOK_URL && !data.HARNESS_AUDIT_WEBHOOK_URL.startsWith("https://") && !data.HARNESS_ALLOW_HTTP) {
    throw new Error(
      `HARNESS_AUDIT_WEBHOOK_URL must use HTTPS (got "${data.HARNESS_AUDIT_WEBHOOK_URL}"). ` +
      "If you need HTTP for local development, set HARNESS_ALLOW_HTTP=true.",
    );
  }

  // Resolve org/project: prefer new names, fall back to deprecated names
  if (!data.HARNESS_ORG && data.HARNESS_DEFAULT_ORG_ID) {
    console.error('[DEPRECATION] HARNESS_DEFAULT_ORG_ID is deprecated. Use HARNESS_ORG instead.');
  }
  if (!data.HARNESS_PROJECT && data.HARNESS_DEFAULT_PROJECT_ID) {
    console.error('[DEPRECATION] HARNESS_DEFAULT_PROJECT_ID is deprecated. Use HARNESS_PROJECT instead.');
  }
  const HARNESS_ORG = data.HARNESS_ORG ?? data.HARNESS_DEFAULT_ORG_ID;
  const HARNESS_PROJECT = data.HARNESS_PROJECT ?? data.HARNESS_DEFAULT_PROJECT_ID;

  // Resolve auto-approve risk: prefer new name, fall back to deprecated SKIP_ELICITATION
  let HARNESS_AUTO_APPROVE_RISK = data.HARNESS_AUTO_APPROVE_RISK ?? "none";
  if (!data.HARNESS_AUTO_APPROVE_RISK && data.HARNESS_SKIP_ELICITATION) {
    console.error(
      '[DEPRECATION] HARNESS_SKIP_ELICITATION is deprecated. Use HARNESS_AUTO_APPROVE_RISK instead.\n' +
      '  HARNESS_SKIP_ELICITATION=true is equivalent to HARNESS_AUTO_APPROVE_RISK=all.',
    );
    HARNESS_AUTO_APPROVE_RISK = "all";
  }

  // Remove deprecated keys from output, expose only the canonical names
  const { HARNESS_DEFAULT_ORG_ID: _oldOrg, HARNESS_DEFAULT_PROJECT_ID: _oldProject, ...rest } = data;

  return { ...rest, HARNESS_API_KEY: data.HARNESS_API_KEY ?? "", HARNESS_ACCOUNT_ID: accountId, HARNESS_ORG, HARNESS_PROJECT, HARNESS_AUTO_APPROVE_RISK };
});

export type Config = z.infer<typeof ConfigSchema>;

/**
 * Resolve the base URL for a given product backend.
 * - "harness" → undefined (uses the default client base URL)
 * - "fme"     → HARNESS_FME_BASE_URL from config (defaults to https://api.split.io)
 */
export function resolveProductBaseUrl(config: Config, product: "harness" | "fme"): string | undefined {
  if (product === "fme") return config.HARNESS_FME_BASE_URL;
  return undefined;
}

export function loadConfig(): Config {
  const result = ConfigSchema.safeParse(process.env);
  if (!result.success) {
    const issues = result.error.issues.map((i) => `  ${i.path.join(".")}: ${i.message}`).join("\n");
    throw new Error(`Invalid configuration:\n${issues}`);
  }

  return result.data;
}
