/*
 * SPDX-FileCopyrightText: 2026 The HedgeDoc developers (see AUTHORS file)
 *
 * SPDX-License-Identifier: AGPL-3.0-only
 */
import { MediaBackendType } from '@hedgedoc/commons';
import { registerAs } from '@nestjs/config';
import z from 'zod';

import { parseOptionalBoolean, printConfigErrorAndExit } from './utils';
import { buildErrorMessage, extractDescriptionFromZodIssue } from './zod-error-message';

const DEFAULT_MAX_UPLOAD_SIZE = 20 * 1024 * 1024; // 20 MB default

const azureSchema = z.object({
  type: z.literal(MediaBackendType.AZURE),
  azure: z.object({
    connectionString: z.string().describe('HD_MEDIA_BACKEND_AZURE_CONNECTION_STRING'),
    container: z.string().describe('HD_MEDIA_BACKEND_AZURE_CONTAINER'),
  }),
});

const filesystemSchema = z.object({
  type: z.literal(MediaBackendType.FILESYSTEM),
  filesystem: z.object({
    uploadPath: z.string().describe('HD_MEDIA_BACKEND_FILESYSTEM_UPLOAD_PATH'),
  }),
});

const imgurSchema = z.object({
  type: z.literal(MediaBackendType.IMGUR),
  imgur: z.object({
    clientId: z.string().describe('HD_MEDIA_BACKEND_IMGUR_CLIENT_ID'),
  }),
});

const s3Schema = z.object({
  type: z.literal(MediaBackendType.S3),
  s3: z.object({
    accessKeyId: z.string().describe('HD_MEDIA_BACKEND_S3_ACCESS_KEY'),
    secretAccessKey: z.string().describe('HD_MEDIA_BACKEND_S3_SECRET_KEY'),
    bucket: z.string().describe('HD_MEDIA_BACKEND_S3_BUCKET'),
    endpoint: z.string().url().describe('HD_MEDIA_BACKEND_S3_ENDPOINT'),
    region: z.string().optional().describe('HD_MEDIA_BACKEND_S3_REGION'),
    pathStyle: z.boolean().default(false).describe('HD_MEDIA_BACKEND_S3_PATH_STYLE'),
  }),
});

const webdavSchema = z.object({
  type: z.literal(MediaBackendType.WEBDAV),
  webdav: z.object({
    connectionString: z.string().url().describe('HD_MEDIA_BACKEND_WEBDAV_CONNECTION_STRING'),
    uploadDir: z.string().optional().describe('HD_MEDIA_BACKEND_WEBDAV_UPLOAD_DIR'),
    publicUrl: z.string().url().describe('HD_MEDIA_BACKEND_WEBDAV_PUBLIC_URL'),
  }),
});

const schema = z.object({
  backend: z.discriminatedUnion('type', [
    azureSchema,
    filesystemSchema,
    imgurSchema,
    s3Schema,
    webdavSchema,
  ]),
  maxUploadSize: z
    .number()
    .min(0)
    .default(DEFAULT_MAX_UPLOAD_SIZE)
    .describe('HD_MEDIA_MAX_UPLOAD_SIZE'),
});

export type MediaConfig = z.infer<typeof schema>;
export type AzureMediaConfig = z.infer<typeof azureSchema>;
export type FilesystemMediaConfig = z.infer<typeof filesystemSchema>;
export type ImgurMediaConfig = z.infer<typeof imgurSchema>;
export type S3MediaConfig = z.infer<typeof s3Schema>;
export type WebdavMediaConfig = z.infer<typeof webdavSchema>;

export default registerAs('mediaConfig', () => {
  const mediaConfig = schema.safeParse({
    backend: {
      type: process.env.HD_MEDIA_BACKEND_TYPE,
      filesystem: {
        uploadPath: process.env.HD_MEDIA_BACKEND_FILESYSTEM_UPLOAD_PATH,
      },
      s3: {
        accessKeyId: process.env.HD_MEDIA_BACKEND_S3_ACCESS_KEY,
        secretAccessKey: process.env.HD_MEDIA_BACKEND_S3_SECRET_KEY,
        bucket: process.env.HD_MEDIA_BACKEND_S3_BUCKET,
        endpoint: process.env.HD_MEDIA_BACKEND_S3_ENDPOINT,
        region: process.env.HD_MEDIA_BACKEND_S3_REGION,
        pathStyle: parseOptionalBoolean(process.env.HD_MEDIA_BACKEND_S3_PATH_STYLE),
      },
      azure: {
        connectionString: process.env.HD_MEDIA_BACKEND_AZURE_CONNECTION_STRING,
        container: process.env.HD_MEDIA_BACKEND_AZURE_CONTAINER,
      },
      imgur: {
        clientId: process.env.HD_MEDIA_BACKEND_IMGUR_CLIENT_ID,
      },
      webdav: {
        connectionString: process.env.HD_MEDIA_BACKEND_WEBDAV_CONNECTION_STRING,
        uploadDir: process.env.HD_MEDIA_BACKEND_WEBDAV_UPLOAD_DIR,
        publicUrl: process.env.HD_MEDIA_BACKEND_WEBDAV_PUBLIC_URL,
      },
    },
    maxUploadSize: process.env.HD_MEDIA_MAX_UPLOAD_SIZE
      ? parseInt(process.env.HD_MEDIA_MAX_UPLOAD_SIZE)
      : DEFAULT_MAX_UPLOAD_SIZE,
  });
  if (mediaConfig.error) {
    const errorMessages = mediaConfig.error.errors.map((issue) =>
      extractDescriptionFromZodIssue(issue, 'HD_MEDIA'),
    );
    const errorMessage = buildErrorMessage(errorMessages);
    return printConfigErrorAndExit(errorMessage);
  }
  return mediaConfig.data;
});
