import { isBlurhashValid } from 'blurhash';
import { z } from 'zod';

import { mimeSchema } from './utils';

const blurhashSchema = z.string().superRefine((value, ctx) => {
  const r = isBlurhashValid(value);

  if (!r.result) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: r.errorReason,
    });
  }
});

const baseAttachmentSchema = z.object({
  id: z.string(),
  type: z.string(),
  url: z.string().url().catch(''),
  preview_url: z.string().url().catch(''),
  remote_url: z.string().url().nullable().catch(null),
  description: z.string().catch(''),
  blurhash: blurhashSchema.nullable().catch(null),

  mime_type: mimeSchema.nullable().catch(null),
});

const imageMetaSchema = z.object({
  width: z.number(),
  height: z.number(),
  size: z.string().regex(/\d+x\d+$/).nullable().catch(null),
  aspect: z.number().nullable().catch(null),
});

const imageAttachmentSchema = baseAttachmentSchema.extend({
  type: z.literal('image'),
  meta: z.object({
    original: imageMetaSchema.optional().catch(undefined),
    small: imageMetaSchema.optional().catch(undefined),
    focus: z.object({
      x: z.number().min(-1).max(1),
      y: z.number().min(-1).max(1),
    }).optional().catch(undefined),
  }).catch({}),
});

const videoAttachmentSchema = baseAttachmentSchema.extend({
  type: z.literal('video'),
  meta: z.object({
    duration: z.number().optional().catch(undefined),
    original: imageMetaSchema.extend({
      frame_rate: z.string().regex(/\d+\/\d+$/).nullable().catch(null),
      duration: z.number().nonnegative().nullable().catch(null),
    }).optional().catch(undefined),
    small: imageMetaSchema.optional().catch(undefined),
    // WIP: add rest
  }).catch({}),
});

const gifvAttachmentSchema = baseAttachmentSchema.extend({
  type: z.literal('gifv'),
  meta: z.object({
    duration: z.number().optional().catch(undefined),
    original: imageMetaSchema.optional().catch(undefined),
  }).catch({}),
});

const audioAttachmentSchema = baseAttachmentSchema.extend({
  type: z.literal('audio'),
  meta: z.object({
    duration: z.number().optional().catch(undefined),
    colors: z.object({
      background: z.string().optional().catch(undefined),
      foreground: z.string().optional().catch(undefined),
      accent: z.string().optional().catch(undefined),
      duration: z.number().optional().catch(undefined),
    }).optional().catch(undefined),
    original: z.object({
      duration: z.number().optional().catch(undefined),
      bitrate: z.number().nonnegative().optional().catch(undefined),
    }).optional().catch(undefined),
  }).catch({}),
});

const unknownAttachmentSchema = baseAttachmentSchema.extend({
  type: z.literal('unknown'),
});

/** @see {@link https://docs.joinmastodon.org/entities/MediaAttachment} */
const mediaAttachmentSchema = z.preprocess((data: any) => ({
  mime_type: data.pleroma?.mime_type,
  preview_url: data.url,
  ...data,
}), z.discriminatedUnion('type', [
  imageAttachmentSchema,
  videoAttachmentSchema,
  gifvAttachmentSchema,
  audioAttachmentSchema,
  unknownAttachmentSchema,
]));

type MediaAttachment = z.infer<typeof mediaAttachmentSchema>;

export { blurhashSchema, mediaAttachmentSchema, type MediaAttachment };
