/*  Copyright (C) CZ.NIC, z.s.p.o. and contributors
 *  SPDX-License-Identifier: GPL-2.0-or-later
 *  For more information, see <https://www.knot-dns.cz/>
 */

#pragma once

#include "contrib/time.h"
#include "knot/updates/zone-update.h"
#include "knot/dnssec/context.h"

enum zone_sign_flags {
	ZONE_SIGN_NONE = 0,
	ZONE_SIGN_DROP_SIGNATURES = (1 << 0),
	ZONE_SIGN_KEEP_SERIAL = (1 << 1),
};

typedef enum zone_sign_flags zone_sign_flags_t;

typedef enum {
	KEY_ROLL_ALLOW_KSK_ROLL    = (1 << 0),
	KEY_ROLL_FORCE_KSK_ROLL    = (1 << 1),
	KEY_ROLL_ALLOW_ZSK_ROLL    = (1 << 2),
	KEY_ROLL_FORCE_ZSK_ROLL    = (1 << 3),
	KEY_ROLL_ALLOW_NSEC3RESALT = (1 << 4),
	KEY_ROLL_ALLOW_ALL         = KEY_ROLL_ALLOW_KSK_ROLL |
	                             KEY_ROLL_ALLOW_ZSK_ROLL |
	                             KEY_ROLL_ALLOW_NSEC3RESALT,
	KEY_ROLL_PRESERVE_FUTURE   = (1 << 5),
} zone_sign_roll_flags_t;

typedef struct {
	knot_time_t next_sign;
	knot_time_t next_rollover;
	knot_time_t next_nsec3resalt;
	knot_time_t last_nsec3resalt;
	bool keys_changed;
	bool plan_ds_check;
	bool plan_dnskey_sync;
} zone_sign_reschedule_t;

typedef struct {
	conf_t *conf;
	uint16_t threads;
	knot_time_t now;   // If not zero: adjust "now" to this timestamp.
	bool incremental;
	bool fast;
	bool no_skip_crypto;
	bool log_plan;
} validation_conf_t;

/*!
 * \brief DNSSEC re-sign zone, store new records into changeset. Valid signatures
 *        and NSEC(3) records will not be changed.
 *
 * \param update       Zone Update structure with current zone contents to be updated by signing.
 * \param conf         Knot configuration.
 * \param flags        Zone signing flags.
 * \param roll_flags   Key rollover flags.
 * \param adjust_now   If not zero: adjust "now" to this timestamp.
 * \param reschedule   Signature refresh time of the oldest signature in zone.
 *
 * \return Error code, KNOT_EOK if successful.
 */
int knot_dnssec_zone_sign(zone_update_t *update,
                          conf_t *conf,
                          zone_sign_flags_t flags,
                          zone_sign_roll_flags_t roll_flags,
                          knot_time_t adjust_now,
                          zone_sign_reschedule_t *reschedule);

/*!
 * \brief Sign changeset (inside incremental Zone Update) created by DDNS or so...
 *
 * \param update      Zone Update structure with current zone contents, changes to be signed and to be updated with signatures.
 * \param conf        Knot configuration.
 *
 * \return Error code, KNOT_EOK if successful.
 */
int knot_dnssec_sign_update(zone_update_t *update, conf_t *conf);

/*!
 * \brief Create new NCES3 salt if the old one is too old, and plan next resalt.
 *
 * For given zone, check NSEC3 salt in KASP db and decide if it shall be recreated
 * and tell the user the next time it shall be called.
 *
 * This function is optimized to be called from NSEC3RESALT_EVENT,
 * but also during zone load so that the zone gets loaded already with
 * proper DNSSEC chain.
 *
 * \param ctx           zone signing context
 * \param soa_rrsigs_ok Zone is signed by current active ZSKs.
 * \param salt_changed  output if KNOT_EOK: when was the salt last changed? (either ctx->now or 0)
 * \param when_resalt   output: timestamp when next resalt takes place
 *
 * \return KNOT_E*
 */
int knot_dnssec_nsec3resalt(kdnssec_ctx_t *ctx, bool soa_rrsigs_ok,
                            knot_time_t *salt_changed, knot_time_t *when_resalt);

/*!
 * \brief When DNSSEC signing failed, re-plan on this time.
 *
 * \param ctx    zone signing context
 *
 * \return Timestamp of next signing attempt.
 */
knot_time_t knot_dnssec_failover_delay(const kdnssec_ctx_t *ctx);

/*!
 * \brief Validate zone DNSSEC based on its contents.
 *
 * \param update         Zone update with contents.
 * \param val_conf       Validation configuration.
 *
 * \return KNOT_E*
 */
int knot_dnssec_validate_zone(zone_update_t *update, validation_conf_t *val_conf);
