import DocumentXmlCache from '#models/DocumentXmlCache';
import { XMLBuilder } from 'xmlbuilder2/lib/interfaces.js';
import Dataset from '#models/dataset';
import Strategy from './Strategy.js';
import { DateTime } from 'luxon';
import { builder } from 'xmlbuilder2';

/**
 * This is the description of the interface
 *
 * @interface Conf
 * @member {Model} model holds the current dataset model
 * @member {XMLBuilder} dom holds the current DOM representation
 * @member {Array<string>} excludeFields List of fields to skip on serialization.
 * @member {boolean} excludeEmpty True, if empty fields get excluded from serialization.
 * @member {string} baseUri Base URI for xlink:ref elements
 */
export interface Conf {
    model: Dataset;
    dom?: XMLBuilder;
    excludeFields: Array<string>;
    excludeEmpty: boolean;
    baseUri: string;
}

export default class XmlModel {
    private config: Conf;
    // private strategy = null;
    private cache: DocumentXmlCache | null = null;
    private _caching = false;
    private strategy: Strategy;

    constructor(dataset: Dataset) {
        // $this->strategy = new Strategy();// Opus_Model_Xml_Version1;
        // $this->config = new Conf();
        // $this->strategy->setup($this->config);

        this.config = {
            excludeEmpty: false,
            baseUri: '',
            excludeFields: [],
            model: dataset,
        };

        this.strategy = new Strategy({
            excludeEmpty: true,
            baseUri: '',
            excludeFields: [],
            model: dataset,
        });
    }

    set model(model: Dataset) {
        this.config.model = model;
    }

    public excludeEmptyFields(): void {
        this.config.excludeEmpty = true;
    }

    get xmlCache(): DocumentXmlCache | null {
        return this.cache;
    }

    set xmlCache(cache: DocumentXmlCache) {
        this.cache = cache;
    }

    get caching(): boolean {
        return this._caching;
    }
    set caching(caching: boolean) {
        this._caching = caching;
    }

    public async getDomDocument(): Promise<XMLBuilder | null> {
        const dataset = this.config.model;

        let domDocument: XMLBuilder | null = await this.getDomDocumentFromXmlCache();
        if (domDocument == null) {
            domDocument = await this.strategy.createDomDocument();
            //     domDocument = create({ version: '1.0', encoding: 'UTF-8', standalone: true }, '<root></root>');
            if (this._caching) {
                // caching is desired:
                this.cache = this.cache || new DocumentXmlCache();
                this.cache.document_id = dataset.id;
                this.cache.xml_version = 1; // (int)$this->strategy->getVersion();
                this.cache.server_date_modified = dataset.server_date_modified.toFormat('yyyy-MM-dd HH:mm:ss');
                this.cache.xml_data = domDocument.end();
                await this.cache.save();
            }
            const node = domDocument.find(
                (n) => {
                    const test = n.node.nodeName == 'Rdr_Dataset';
                    return test;
                },
                false,
                true,
            )?.node;
            if (node != undefined) {
                domDocument = builder({ version: '1.0', encoding: 'UTF-8', standalone: true }, node);
            }
        }
        return domDocument;
    }

    private async getDomDocumentFromXmlCache(): Promise<XMLBuilder | null> {
        const dataset: Dataset = this.config.model;
        if (!this.cache) {
            return null;
        }
        //.toFormat('YYYY-MM-DD HH:mm:ss');
        let date: DateTime = dataset.server_date_modified;
        const actuallyCached: boolean = await DocumentXmlCache.hasValidEntry(dataset.id, date);
        if (!actuallyCached) {
            return null;
        }
        //cache is actual return it for oai:
        try {
            if (this.cache) {
                return this.cache.getDomDocument();
            } else {
                return null;
            }
        } catch (error) {
            return null;
        }
    }
}
