import { z } from 'zod';

export const BackgroundImageSchema = z.looseObject({
  /** REQUIRED. A URI pointing to the background image. */
  uri: z.string(),
  /** OPTIONAL. An "integrity metadata" string as described in Section 7. */
  'uri#integrity': z.string().optional(),
});
export type BackgroundImage = z.infer<typeof BackgroundImageSchema>;

/**
 * Logo metadata used in rendering a credential.
 */
export const LogoSchema = z.looseObject({
  /** REQUIRED. A URI pointing to the logo image. */
  uri: z.string(),
  /** OPTIONAL. An "integrity metadata" string as described in Section 7. */
  'uri#integrity': z.string().optional(),
  /** OPTIONAL. A string containing alternative text for the logo image. */
  alt_text: z.string().optional(),
});

export type Logo = z.infer<typeof LogoSchema>;

/**
 * The simple rendering method is intended for applications that do not support SVG.
 */
export const SimpleRenderingSchema = z.looseObject({
  /** OPTIONAL. Logo metadata to display for the credential. */
  logo: LogoSchema.optional(),
  /** OPTIONAL. RGB color value for the credential background (e.g., "#FFFFFF"). */
  background_color: z.string().optional(),
  /** OPTIONAL. RGB color value for the credential text (e.g., "#000000"). */
  text_color: z.string().optional(),
  /** OPTIONAL. An object containing information about the background image to be displayed for the type. */
  background_image: BackgroundImageSchema.optional(),
});

export type SimpleRendering = z.infer<typeof SimpleRenderingSchema>;

/** Enum of valid values for rendering orientation. */
export const OrientationSchema = z.enum(['portrait', 'landscape']);

/** Enum of valid values for rendering color schemes. */
export const ColorSchemeSchema = z.enum(['light', 'dark']);

/** Enum of valid values for rendering contrast. */
export const ContrastSchema = z.enum(['normal', 'high']);

/**
 * Properties that describe the display preferences for an SVG template rendering.
 */
export const SvgTemplatePropertiesSchema = z.looseObject({
  /** OPTIONAL. Orientation optimized for the template. */
  orientation: OrientationSchema.optional(),
  /** OPTIONAL. Color scheme optimized for the template. */
  color_scheme: ColorSchemeSchema.optional(),
  /** OPTIONAL. Contrast level optimized for the template. */
  contrast: ContrastSchema.optional(),
});

export type SvgTemplateProperties = z.infer<typeof SvgTemplatePropertiesSchema>;

/**
 * SVG rendering metadata containing URI and optional integrity and properties.
 */
export const SvgTemplateRenderingSchema = z.looseObject({
  /** REQUIRED. A URI pointing to the SVG template. */
  uri: z.string(),
  /** OPTIONAL. An "integrity metadata" string as described in Section 7. */
  'uri#integrity': z.string().optional(),
  /** REQUIRED if more than one SVG template is present. */
  properties: SvgTemplatePropertiesSchema.optional(),
});

export type SvgTemplateRendering = z.infer<typeof SvgTemplateRenderingSchema>;

/**
 * Rendering metadata, either simple or SVG-based, for a credential.
 */
export const RenderingSchema = z
  .looseObject({
    /** OPTIONAL. Simple rendering metadata. */
    simple: SimpleRenderingSchema.optional(),
    /** OPTIONAL. Array of SVG template rendering objects. */
    svg_templates: z.array(SvgTemplateRenderingSchema).optional(),
    /**
     * OPTIONAL. Array of SVG template rendering objects.
     * @deprecated use `svg_templates` (plural) instead.
     */
    svg_template: z.array(SvgTemplateRenderingSchema).optional(),
  })
  .transform(({ svg_template, svg_templates, ...rest }) => ({
    ...rest,
    svg_templates: svg_templates ?? svg_template,
  }));

export type Rendering = z.infer<typeof RenderingSchema>;

/**
 * Display metadata associated with a credential type.
 */
export const DisplaySchema = z
  .looseObject({
    /**
     * Language tag according to RFC 5646 (e.g., "en", "de").
     * @deprecated - use `locale` instead
     */
    lang: z.string().optional(),

    /**
     * REQUIRED (preferred). Language tag according to RFC 5646.
     * Alias for `lang` - either `lang` or `locale` must be provided.
     */
    locale: z.string().optional(),

    /** REQUIRED. Human-readable name for the credential type. */
    name: z.string(),
    /** OPTIONAL. Description of the credential type for end users. */
    description: z.string().optional(),
    /** OPTIONAL. Rendering information (simple or SVG) for the credential. */
    rendering: RenderingSchema.optional(),
  })
  .transform(({ lang, locale, ...rest }) => ({
    ...rest,
    locale: locale ?? lang,
  }))
  .refine(({ locale }) => locale !== undefined, {
    message:
      'Either locale (preferred) or lang (spec name, deprecated) MUST be defined on claim display entry.',
  });

export type Display = z.infer<typeof DisplaySchema>;

/**
 * Claim path within the credential's JSON structure.
 * Example: ["address", "street_address"]
 */
export const ClaimPathSchema = z.array(z.string().nullable());

export type ClaimPath = z.infer<typeof ClaimPathSchema>;

/**
 * Display metadata for a specific claim.
 */
export const ClaimDisplaySchema = z
  .looseObject({
    /**
     * Language tag according to RFC 5646.
     * @deprecated - use `locale` instead
     */
    lang: z.string().optional(),

    /**
     * REQUIRED (preferred). Language tag according to RFC 5646.
     * Alias for `lang` - either `lang` or `locale` must be provided.
     */
    locale: z.string().optional(),

    /** REQUIRED. Human-readable label for the claim. */
    label: z.string().optional(),

    /**
     * Human-readable label for the claim.
     * @deprecated - use `label` instead
     */
    name: z.string().optional(),

    /** OPTIONAL. Description of the claim for end users. */
    description: z.string().optional(),
  })
  .transform(({ lang, name, label, locale, ...rest }) => ({
    ...rest,
    locale: locale ?? lang,
    label: label ?? name,
  }))
  .refine(({ locale }) => locale !== undefined, {
    message:
      'Either locale (preferred) or lang (deprecated) MUST be defined on claim display entry.',
  })
  .refine(({ label }) => label !== undefined, {
    message:
      'Either label (preferred) or name (deprecated) MUST be defined on claim display entry.',
  });

export type ClaimDisplay = z.infer<typeof ClaimDisplaySchema>;

/**
 * Indicates whether a claim is selectively disclosable.
 */
export const ClaimSelectiveDisclosureSchema = z.enum([
  'always',
  'allowed',
  'never',
]);

export type ClaimSelectiveDisclosure = z.infer<
  typeof ClaimSelectiveDisclosureSchema
>;

/**
 * Metadata for individual claims in the credential type.
 */
export const ClaimSchema = z.looseObject({
  /**
   * REQUIRED. Array of one or more paths to the claim in the credential subject.
   * Each path is an array of strings (or null for array elements).
   */
  path: ClaimPathSchema,
  /** OPTIONAL. Display metadata in multiple languages. */
  display: z.array(ClaimDisplaySchema).optional(),
  /** OPTIONAL. Controls whether the claim must, may, or must not be selectively disclosed. */
  sd: ClaimSelectiveDisclosureSchema.optional(),
  /**
   * OPTIONAL. A boolean indicating whether the claim must be present in the Unsecured Payload
   * of the SD-JWT VC. Default is false if not specified.
   */
  mandatory: z.boolean().optional(),
  /**
   * OPTIONAL. Unique string identifier for referencing the claim in an SVG template.
   * Must consist of alphanumeric characters or underscores and must not start with a digit.
   */
  svg_id: z.string().optional(),
});

export type Claim = z.infer<typeof ClaimSchema>;

/**
 * Type metadata for a specific Verifiable Credential (VC) type.
 * Reference: https://www.ietf.org/archive/id/draft-ietf-oauth-sd-jwt-vc-13.html#name-type-metadata-format
 */
export const TypeMetadataFormatSchema = z.looseObject({
  /** REQUIRED. A URI uniquely identifying the credential type. */
  vct: z.string(),
  /** OPTIONAL. Human-readable name for developers. */
  name: z.string().optional(),
  /** OPTIONAL. Human-readable description for developers. */
  description: z.string().optional(),
  /** OPTIONAL. URI of another type that this one extends. */
  extends: z.string().optional(),
  /** OPTIONAL. Integrity metadata for the 'extends' field. */
  'extends#integrity': z.string().optional(),
  /** OPTIONAL. Array of localized display metadata for the type. */
  display: z.array(DisplaySchema).optional(),
  /** OPTIONAL. Array of claim metadata. */
  claims: z.array(ClaimSchema).optional(),
});

/**
 * The resolved type metadata. If you just want to use the type metadata, you should use `typeMetadata`.
 * In case additional processing is needed (e.g. for extensions in type metadata), you can use the `typeMetadataChain`
 */
export type ResolvedTypeMetadata = {
  /**
   * The merged type metadata based on the resolved `vct` document and all `extends` values.
   */
  mergedTypeMetadata: TypeMetadataFormat;

  /**
   * The original type metadata documents, ordered from the extending type to the last extended type.
   */
  typeMetadataChain: [TypeMetadataFormat, ...TypeMetadataFormat[]];

  /**
   * The vct values present in the type metadata chain. This can be used for matching against e.g.
   * DCQL queries which can query an underlying type.
   */
  vctValues: [string, ...string[]];
};

export type TypeMetadataFormat = z.infer<typeof TypeMetadataFormatSchema>;
