import * as path from "path";
import { AttachmentNativeBinding, MessageBuilderNativeBinding } from "../types";
import { NodeTrustedMailcore2NativeBinding } from '../types/NodeTrustedMailcore2NativeBinding'
import { Attachment } from './Attachment';
import { MessageHeader } from './MessageHeader';

// tslint:disable-next-line: no-var-requires
let bindings: NodeTrustedMailcore2NativeBinding;
try {
    bindings = require(path.join(__dirname, "../../lib/binding/trusted_mailcore2.node"));
} catch(e) {
    bindings = require(path.join(__dirname, "../../lib/binding/trusted_mailcore2_no_crypto.node"));
}

// tslint:disable-next-line
const { MessageBuilder: _MessageBuilder } = bindings;

/**
 * MessageBuilder Class
 *
 * @public
 */
class MessageBuilder extends bindings.MessageBuilder {
    /**
     * Wrapping object handle
     *
     * @internal
     */
    public handle: MessageBuilderNativeBinding;

    constructor() {
        super();

        const handle = new _MessageBuilder();
        this.handle = handle;
    }

    /**
     * Message header getter
     *
     *
     * @type {MessageHeader}
     * @memberof MessageBuilder
     */
    get header(): MessageHeader | undefined {
        if (undefined === this.handle.header) {
            return undefined;
        }

        const value = new MessageHeader();
        value.handle = this.handle.header;

        return value;
    }

    /**
     * Message header setter
     *
     *
     * @param {MessageHeader}  msgHeader
     * @memberof MessageBuilder
     */
    set header(msgHeader: MessageHeader | undefined) {
        this.handle.header = msgHeader ? msgHeader.handle : undefined;
    }

    /**
     * HTML body getter
     *
     *
     * @type {string}
     * @memberof MessageBuilder
     */
    get htmlBody(): string {
        return this.handle.htmlBody;
    }

    /**
     * HTML body setter
     *
     *
     * @type {string | Buffer}
     * @memberof MessageBuilder
     */
    set htmlBody(bodyVal: string) {
        this.handle.htmlBody = bodyVal;
    }

    /**
     * Text body getter
     *
     *
     * @type {string}
     * @memberof MessageBuilder
     */
    get textBody(): string {
        return this.handle.textBody;
    }

    /**
     * Text body setter
     *
     *
     * @type {string}
     * @memberof MessageBuilder
     */
    set textBody(bodyVal: string) {
        this.handle.textBody = bodyVal;
    }

    /**
     * Attachments getter
     *
     *
     * @type {Attachment[]}
     * @memberof MessageBuilder
     */
    get attachments(): Attachment[] {
        const attaches: AttachmentNativeBinding[] = this.handle.attachments;
        const result: Attachment[] = [];

        if (undefined === attaches) {
            return result;
        }

        attaches.forEach((item: AttachmentNativeBinding) => {
            const value = new Attachment();
            value.handle = item;
            result.push(value);
        })
        return result;
    }

    /**
     * Attachments setter
     *
     *
     * @type {Attachment[]}
     * @memberof MessageBuilder
     */
    set attachments(attaches: Attachment[]) {
        const result: AttachmentNativeBinding[] = [];
        attaches.forEach((item: Attachment) => {
            result.push(item.handle);
        })
        this.handle.attachments = result;
    }

    /**
     * Method to add attachments
     *
     *
     * @param {Attachment[]} attaches attachments to add
     * @memberof MessageBuilder
     */
    public addAttachments(attaches: Attachment[]): void {
        const result: AttachmentNativeBinding[] = [];
        attaches.forEach((item: Attachment) => {
            result.push(item.handle);
        })
        this.handle.addAttachments(result);
    }

    /**
     * Related attachments getter
     *
     *
     * @type {Attachment[]}
     * @memberof MessageBuilder
     */
    get relatedAttachments(): Attachment[] {
        const attaches: AttachmentNativeBinding[] = this.handle.relatedAttachments;
        const result: Attachment[] = [];

        if (undefined === attaches) {
            return result;
        }

        attaches.forEach((item: AttachmentNativeBinding) => {
            const value = new Attachment();
            value.handle = item;
            result.push(value);
        })
        return result;
    }

    /**
     * related Attachments setter
     *
     *
     * @type {Attachment[]}
     * @memberof MessageBuilder
     */
    set relatedAttachments(attaches: Attachment[]) {
        const result: AttachmentNativeBinding[] = [];
        attaches.forEach((item: Attachment) => {
            result.push(item.handle);
        })
        this.handle.relatedAttachments = result;
    }

    /**
     * Method to add related attachments
     *
     *
     * @param {Attachment[]} attaches attachments to add as related to body
     * @memberof MessageBuilder
     */
    public addRelatedAttachments(attaches: Attachment[]): void {
        const result: AttachmentNativeBinding[] = [];
        attaches.forEach((item: Attachment) => {
            result.push(item.handle);
        })
        this.handle.addRelatedAttachments(result);
    }

    /**
     * Boundary prefix getter
     *
     *
     * @type {string}
     * @memberof MessageBuilder
     */
    get boundaryPrefix(): string {
        return this.handle.boundaryPrefix;
    }

    /**
     * Boundary prefix setter
     *
     *
     * @type {string}
     * @memberof MessageBuilder
     */
    set boundaryPrefix(prefVal: string) {
        this.handle.boundaryPrefix = prefVal;
    }

    /**
     * Builded message data getter
     *
     *
     * @type {Buffer}
     * @memberof MessageBuilder
     */
    get data(): Buffer {
        return this.handle.data;
    }

    /**
     * Asynchronous method to get message data
     *
     *
     * @type {Buffer}
     * @memberof MessageBuilder
     */
    public getDataAsync(): Promise<Buffer> {
        return new Promise((resolve, reject) => {
            this.handle.getDataAsync((error: string | Error | undefined | null, result: Buffer) => {
                if (error) {
                    reject(error);
                }

                resolve(result);
            });
        });
    }

    /**
     * Method to write message data
     *
     *
     * @param {string} fileName file path to save data
     * @memberof MessageBuilder
     */
    public Write(fileName: string): void {
        this.handle.Write(fileName);
    }

    /**
     * Method to write message data asynchronously
     *
     *
     * @param {string} fileName file path to save data
     * @memberof MessageBuilder
     */
    public WriteAsync(fileName: string): Promise<void> {
        return new Promise<void>((resolve, reject) => {
            this.handle.WriteAsync(fileName, (error: string | Error | undefined | null) => {
                if (error) {
                    reject(error);
                }

                resolve();
            });
        });
    }

    /**
     * Method to set signature certificate
     *
     *
     * @param {string} certData Signer's certificate blob
     * @param {string} signerMail Signer's mail address (not a very useful parameter)
     * @memberof MessageBuilder
     */
    public SetSignerCert(certData: string, signerMail?: string): void {
        this.handle.SetSignerCert(certData, signerMail);
    }

    /**
     * Method to set recipients certificates array
     *
     *
     * @param {string[]} certData Array of recipients certificate blobs
     * @memberof MessageBuilder
     */
    public SetRecipientsCerts(certsData: string[]): void {
        this.handle.SetRecipientsCerts(certsData);
    }

    /**
     * Key usage policy getter
     *
     *
     * @type {"kupNone" | "kupCheckKeyEnc" | "kupCheckKeyAgree" | "kupCheckOneOf"}
     * @memberof MessageBuilder
     */
    get keyUsagePolicy(): "kupNone" | "kupCheckKeyEnc" | "kupCheckKeyAgree" | "kupCheckOneOf" {
        return this.handle.keyUsagePolicy;
    }

    /**
     * Key usage policy setter
     *
     *
     * @type {"kupNone" | "kupCheckKeyEnc" | "kupCheckKeyAgree" | "kupCheckOneOf"}
     * @memberof MessageBuilder
     */
    set keyUsagePolicy(policyVal: "kupNone" | "kupCheckKeyEnc" | "kupCheckKeyAgree" | "kupCheckOneOf") {
        this.handle.keyUsagePolicy = policyVal;
    }

    /**
     * Method to read data from memory
     *
     *
     * @param {Buffer | string} data memory buffer to read data
     * @memberof MessageBuilder
     */
    public SetTempFolder(tmpFolderPath: string): void {
        this.handle.SetTempFolder(tmpFolderPath);
    }

    /**
     * Method to set license for portable version
     *
     *
     * @param {string} data license
     * @memberof MessageBuilder
     */
    public static SetLicense(lic: string): void {
        bindings.MessageBuilder.SetLicense(lic);
    }
}

export { MessageBuilder }
