#pragma once

#include "io.h"

#include <util/datetime/base.h>
#include <util/generic/array_ref.h>
#include <util/generic/ptr.h>
#include <util/generic/string.h>
#include <util/generic/vector.h>

#include <array>

class IInputStream;

namespace NSv {
    struct TCertDescr {
        TString KeyFile;
        TString CertFile;

        TCertDescr(TString all) : KeyFile(all), CertFile(all) {}
        TCertDescr(TString key, TString cert) : KeyFile(key), CertFile(cert) {}
    };

    // Parse a ticket key file, which must be either 48 bytes long (a single key) or
    // in PEM format with SESSION TICKET KEY entries.
    TVector<std::array<ui8, 48>> ReadTicketKeys(IInputStream&);

    struct TTLSOptions {
        // Whether this is a client-side context, i.e. the socket it wraps should be `connect`ed.
        bool Client = false;
        // Ask the other side for a valid certificate.
        bool VerifyPeer = Client;
        // When a server-side context, *require* a certificate rather than just ask.
        // (Server certificates are always required if verification is enabled.)
        bool RequireClientCert = false;
        // Certificates to provide. Optional for client-side contexts.
        TArrayRef<const TCertDescr> Certs;
        // If non-empty, use ALPN to negotiate a protocol while establishing a connection.
        // The protocols are sorted by decreasing priority.
        TArrayRef<const TStringBuf> Protocols;
        // Ciphers to use, in OpenSSL cipher list format. If empty, defaults to
        // some suites with PFS with preference for elliptic curves.
        TString Ciphers;
        // Keys used for encrypting session tickets. The first entry is used for new ones,
        // the others are only for decrypting old ones. Default is a single random key.
        TArrayRef<const std::array<ui8, 48>> TicketKeys;
        TDuration TicketTTL = TDuration::Seconds(28800);
    };

    struct TTLSContext {
    public:
        TTLSContext(TTLSOptions = {});

        // (SERVER ONLY) Add the next entry to the SNI chain. When a client requests to connect to
        // a particular virtual host, the first context in the chain (starting with this one)
        // that contains a certificate valid for that hostname is selected. If none matches,
        // the last context is used. NOTE: the tail of the chain must outlive the head.
        void SetNext(const TTLSContext&);

        // Wrap a file descriptor into an encrypted I/O object.
        // (CLIENT ONLY) set `hostname` to check that the certificate matches the desired domain.
        THolder<NSv::IO> Wrap(int fd, TMaybe<TString> hostname = {}) const noexcept;

    private:
        struct TImpl;
        // Unlike providing `~TTLSContext`, this does not default-delete move constructors.
        struct TDestroy { static void Destroy(TImpl*) noexcept; };
        THolder<TImpl, TDestroy> I_;
    };
}
