/// <reference types="../global.d.ts" />

import { createPrivateKey, createPublicKey } from 'node:crypto';
import { homedir } from 'node:os';
import { join } from 'node:path';

import parse from 'parse-duration';

import pkg from '../../package.json' assert { type: 'json' };
import type { AFFiNEConfig, ServerFlavor } from './def';
import { applyEnvToConfig } from './env';

export const SERVER_FLAVOR = (process.env.SERVER_FLAVOR ??
  'allinone') as ServerFlavor;

// Don't use this in production
export const examplePrivateKey = `-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIEtyAJLIULkphVhqXqxk4Nr8Ggty3XLwUJWBxzAWCWTMoAoGCCqGSM49
AwEHoUQDQgAEF3U/0wIeJ3jRKXeFKqQyBKlr9F7xaAUScRrAuSP33rajm3cdfihI
3JvMxVNsS2lE8PSGQrvDrJZaDo0L+Lq9Gg==
-----END EC PRIVATE KEY-----`;

const jwtKeyPair = (function () {
  const AUTH_PRIVATE_KEY = process.env.AUTH_PRIVATE_KEY ?? examplePrivateKey;
  const privateKey = createPrivateKey({
    key: Buffer.from(AUTH_PRIVATE_KEY),
    format: 'pem',
    type: 'sec1',
  })
    .export({
      format: 'pem',
      type: 'pkcs8',
    })
    .toString('utf8');
  const publicKey = createPublicKey({
    key: Buffer.from(AUTH_PRIVATE_KEY),
    format: 'pem',
    type: 'spki',
  })
    .export({
      format: 'pem',
      type: 'spki',
    })
    .toString('utf8');

  return {
    publicKey,
    privateKey,
  };
})();

export const getDefaultAFFiNEConfig: () => AFFiNEConfig = () => {
  const defaultConfig = {
    serverId: 'affine-nestjs-server',
    version: pkg.version,
    ENV_MAP: {
      AFFINE_SERVER_PORT: ['port', 'int'],
      AFFINE_SERVER_HOST: 'host',
      AFFINE_SERVER_SUB_PATH: 'path',
      AFFINE_ENV: 'affineEnv',
      DATABASE_URL: 'db.url',
      ENABLE_R2_OBJECT_STORAGE: ['objectStorage.r2.enabled', 'boolean'],
      R2_OBJECT_STORAGE_ACCOUNT_ID: 'objectStorage.r2.accountId',
      R2_OBJECT_STORAGE_ACCESS_KEY_ID: 'objectStorage.r2.accessKeyId',
      R2_OBJECT_STORAGE_SECRET_ACCESS_KEY: 'objectStorage.r2.secretAccessKey',
      R2_OBJECT_STORAGE_BUCKET: 'objectStorage.r2.bucket',
      ENABLE_CAPTCHA: ['auth.captcha.enable', 'boolean'],
      CAPTCHA_TURNSTILE_SECRET: ['auth.captcha.turnstile.secret', 'string'],
      OAUTH_GOOGLE_ENABLED: ['auth.oauthProviders.google.enabled', 'boolean'],
      OAUTH_GOOGLE_CLIENT_ID: 'auth.oauthProviders.google.clientId',
      OAUTH_GOOGLE_CLIENT_SECRET: 'auth.oauthProviders.google.clientSecret',
      OAUTH_GITHUB_ENABLED: ['auth.oauthProviders.github.enabled', 'boolean'],
      OAUTH_GITHUB_CLIENT_ID: 'auth.oauthProviders.github.clientId',
      OAUTH_GITHUB_CLIENT_SECRET: 'auth.oauthProviders.github.clientSecret',
      OAUTH_EMAIL_LOGIN: 'auth.email.login',
      OAUTH_EMAIL_SENDER: 'auth.email.sender',
      OAUTH_EMAIL_SERVER: 'auth.email.server',
      OAUTH_EMAIL_PORT: ['auth.email.port', 'int'],
      OAUTH_EMAIL_PASSWORD: 'auth.email.password',
      THROTTLE_TTL: ['rateLimiter.ttl', 'int'],
      THROTTLE_LIMIT: ['rateLimiter.limit', 'int'],
      REDIS_SERVER_ENABLED: ['redis.enabled', 'boolean'],
      REDIS_SERVER_HOST: 'redis.host',
      REDIS_SERVER_PORT: ['redis.port', 'int'],
      REDIS_SERVER_USER: 'redis.username',
      REDIS_SERVER_PASSWORD: 'redis.password',
      REDIS_SERVER_DATABASE: ['redis.database', 'int'],
      DOC_MERGE_INTERVAL: ['doc.manager.updatePollInterval', 'int'],
      DOC_MERGE_USE_JWST_CODEC: [
        'doc.manager.experimentalMergeWithJwstCodec',
        'boolean',
      ],
      ENABLE_LOCAL_EMAIL: ['auth.localEmail', 'boolean'],
      STRIPE_API_KEY: 'payment.stripe.keys.APIKey',
      STRIPE_WEBHOOK_KEY: 'payment.stripe.keys.webhookKey',
      FEATURES_EARLY_ACCESS_PREVIEW: [
        'featureFlags.earlyAccessPreview',
        'boolean',
      ],
    } satisfies AFFiNEConfig['ENV_MAP'],
    affineEnv: 'dev',
    get affine() {
      const env = this.affineEnv;
      return {
        canary: env === 'dev',
        beta: env === 'beta',
        stable: env === 'production',
      };
    },
    env: process.env.NODE_ENV ?? 'development',
    get node() {
      const env = this.env;
      return {
        prod: env === 'production',
        dev: env === 'development',
        test: env === 'test',
      };
    },
    get deploy() {
      return !this.node.dev && !this.node.test;
    },
    featureFlags: {
      earlyAccessPreview: false,
    },
    get https() {
      return !this.node.dev;
    },
    host: 'localhost',
    port: 3010,
    path: '',
    db: {
      url: '',
    },
    get origin() {
      return this.node.dev
        ? 'http://localhost:8080'
        : `${this.https ? 'https' : 'http'}://${this.host}${
            this.host === 'localhost' ? `:${this.port}` : ''
          }`;
    },
    get baseUrl() {
      return `${this.origin}${this.path}`;
    },
    graphql: {
      buildSchemaOptions: {
        numberScalarMode: 'integer',
      },
      introspection: true,
      playground: true,
    },
    auth: {
      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
      accessTokenExpiresIn: parse('1h')! / 1000,
      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
      refreshTokenExpiresIn: parse('7d')! / 1000,
      leeway: 60,
      captcha: {
        enable: false,
        turnstile: {
          secret: '1x0000000000000000000000000000000AA',
        },
        challenge: {
          bits: 20,
        },
      },
      privateKey: jwtKeyPair.privateKey,
      publicKey: jwtKeyPair.publicKey,
      enableSignup: true,
      enableOauth: false,
      get nextAuthSecret() {
        return this.privateKey;
      },
      oauthProviders: {},
      localEmail: false,
      email: {
        server: 'smtp.gmail.com',
        port: 465,
        login: '',
        sender: '',
        password: '',
      },
    },
    objectStorage: {
      r2: {
        enabled: false,
        bucket: '',
        accountId: '',
        accessKeyId: '',
        secretAccessKey: '',
      },
      fs: {
        path: join(homedir(), '.affine-storage'),
      },
    },
    rateLimiter: {
      ttl: 60,
      limit: 60,
    },
    redis: {
      enabled: false,
      host: '127.0.0.1',
      port: 6379,
      username: '',
      password: '',
      database: 0,
    },
    doc: {
      manager: {
        enableUpdateAutoMerging: SERVER_FLAVOR !== 'sync',
        updatePollInterval: 3000,
        experimentalMergeWithJwstCodec: false,
      },
      history: {
        interval: 1000 * 60 * 10 /* 10 mins */,
      },
    },
    payment: {
      stripe: {
        keys: {
          APIKey: '',
          webhookKey: '',
        },
        apiVersion: '2023-10-16',
      },
    },
  } satisfies AFFiNEConfig;

  applyEnvToConfig(defaultConfig);

  return defaultConfig;
};
