import process from "node:process";
import convict, { SchemaObj } from "convict";
import { IServerConfig, ServerConfig } from "./server.js";
import { IMetricsConfig, MetricsConfig } from "./metrics-server.js";
import { IAppMetaConfig, AppMetaConfig } from "./app-meta.js";
import { ICorsConfig, CorsConfig } from "./cors.js";
import { ILoggingConfig, LoggingConfig } from "./logging.js";
import { ExtendedConvict } from "./types.js";
import { MetamigoConvictFormats } from "./formats.js";

type IEnvConfig = "production" | "development" | "test";

const EnvConfig: SchemaObj<IEnvConfig> = {
  doc: "The application environment",
  format: ["production", "development", "test"],
  default: "development",
  env: "NODE_ENV",
};

export const configBaseSchema = {
  env: EnvConfig,
  server: ServerConfig,
  meta: AppMetaConfig,
  cors: CorsConfig,
  metrics: MetricsConfig,
  logging: LoggingConfig,
};

/**
 *
 * The metamigo base configuration object. Use this for easy typed access to your
 * config.
 *
 */
interface IMetamigoConfig {
  env: IEnvConfig;
  server: IServerConfig;
  meta: IAppMetaConfig;
  cors: ICorsConfig;
  metrics: IMetricsConfig;
  logging: ILoggingConfig;
  isProd?: boolean;
  isTest?: boolean;
  isDev?: boolean;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  frontend: any;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  nextAuth: any;
}
export type IMetamigoConvict = ExtendedConvict<IMetamigoConfig>;

export type { IMetamigoConfig };

export * from "./formats.js";
export * from "./generate.js";
export * from "./print.js";
export * from "./types.js";

/**
 * Loads your applications configuration from environment variables and configuration files (see METAMIGO_CONFIG).
 *
 * @param schema your schema definition
 * @param override an optional object with config value that will override defaults but not  config files and env vars (see [convict precedence docs](https://github.com/mozilla/node-convict/tree/master/packages/convict#precedence-order ))
 * @returns the raw convict config object
 */
export const loadConfigurationRaw = async <T extends IMetamigoConfig>(
  schema: convict.Schema<T>,
  override?: Partial<T>
): Promise<ExtendedConvict<T>> => {
  convict.addFormats(MetamigoConvictFormats);
  const config: ExtendedConvict<T> = convict(schema);

  const env = config.get("env");

  config.isProd = env === "production";
  config.isTest = env === "test";
  config.isDev = env === "development";

  try {
    if (process.env.METAMIGO_CONFIG) {
      config.loadFile(process.env.METAMIGO_CONFIG);
    }
  } catch (error) {
    const msg = `


🚫 Your application's configuration is invalid JSON. 🚫

${error}

`;
    throw new Error(msg);
  }

  if (override) {
    config.load(override);
  }

  try {
    config.validate({ allowed: "strict" });
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
  } catch (error: any) {
    const msg = `


🚫 Your application's configuration is invalid. 🚫

${error.message}

`;
    throw new Error(msg);
  }

  // set our helpers
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const configDirty = config as any;

  configDirty.set("isProd", config.isProd);
  configDirty.set("isTest", config.isTest);
  configDirty.set("isDev", config.isDev);

  return config;
};

/**
 * Loads your applications configuration from environment variables and configuration files (see METAMIGO_CONFIG).
 *
 * @param schema your schema definition
 * @param override an optional object with config value that will override defaults but not config files and env vars (see [convict precedence docs](https://github.com/mozilla/node-convict/tree/master/packages/convict#precedence-order ))
 * @returns a vanilla javascript object with the config loaded values
 */
export const loadConfiguration = async <T extends IMetamigoConfig>(
  schema: convict.Schema<T>,
  override?: Partial<T>
): Promise<T> => {
  const c = await loadConfigurationRaw(schema, override);
  return c.getProperties();
};

export { type IServerConfig } from "./server.js";
export { type IMetricsConfig } from "./metrics-server.js";
export { type IAppMetaConfig } from "./app-meta.js";
export { type ICorsConfig } from "./cors.js";
export { type ILoggingConfig } from "./logging.js";
