import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import { RequestContract } from '@ioc:Adonis/Core/Request';
import { XMLBuilder } from 'xmlbuilder2/lib/interfaces';
import { create } from 'xmlbuilder2';
import dayjs, { Dayjs } from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
import { readFileSync } from 'fs';
import { StatusCodes } from 'http-status-codes';
import { transform } from 'saxon-js';
// import { Xslt, xmlParse } from 'xslt-processor'
import { OaiErrorCodes, OaiModelError } from 'App/Exceptions/OaiErrorCodes';
import { OaiModelException } from 'App/Exceptions/OaiModelException';

interface XslTParameter {
    [key: string]: any;
}

interface Dictionary {
    [index: string]: string;
}

export default class OaiController {
    // private deliveringDocumentStates = ["published", "deleted"];
    // private sampleRegEx = /^[A-Za-zäüÄÜß0-9\-_.!~]+$/;
    private xsltParameter: XslTParameter;
    /**
     * Holds xml representation of document information to be processed.
     *
     * @var xmlbuilder.XMLDocument | null  Defaults to null.
     */
    private xml: XMLBuilder;
    private proc;

    constructor() {
        // Load the XSLT file
        this.proc = readFileSync('public/assets2/datasetxml2oai.sef.json');
        // tests
        // const xslPath = 'assets/datasetxml2oai-pmh.xslt'; // Replace with the actual path to your XSLT file
        // this.proc = readFileSync(xslPath, 'utf-8');
        // this.configuration = new Configuration();
        dayjs.extend(utc);
        dayjs.extend(timezone);
    }

    public async index({ response, request }: HttpContextContract): Promise<void> {
        this.xml = create({ version: '1.0', encoding: 'UTF-8', standalone: true }, '<root></root>');

        // this.proc = new XSLTProcessor();
        // const stylesheet = readFileSync(__dirname + "/datasetxml2oai.sef.json");
        const xsltParameter = (this.xsltParameter = {});

        let oaiRequest: Dictionary = {};
        if (request.method() === 'POST') {
            oaiRequest = request.body();
        } else if (request.method() === 'GET') {
            oaiRequest = request.qs();
        } else {
            xsltParameter['oai_error_code'] = 'unknown';
            xsltParameter['oai_error_message'] = 'Only POST and GET methods are allowed for OAI-PMH.';
        }
        // const oaiRequest: OaiParameter = request.body;
        try {
            this.handleRequest(oaiRequest, request);
        } catch (error) {
            if (error instanceof OaiModelException) {
                const code = error.oaiCode;
                let oaiErrorCode: string | undefined = 'Unknown oai error code ' + code;
                if (OaiModelError.has(error.oaiCode) && OaiModelError.get(code) !== undefined) {
                    oaiErrorCode = OaiModelError.get(error.oaiCode);
                }
                this.xsltParameter['oai_error_code'] = oaiErrorCode;
                this.xsltParameter['oai_error_message'] = error.message;
            } else {
                //     // return next(error); // passing to default express middleware error handler
                this.xsltParameter['oai_error_code'] = 'unknown';
                this.xsltParameter['oai_error_message'] = 'An internal error occured.';
            }
        }

        const xmlString = this.xml.end({ prettyPrint: true });

        let xmlOutput;
        try {
            const result = await transform({
                // stylesheetFileName: `${config.TMP_BASE_DIR}/data-quality/rules/iati.sef.json`,
                stylesheetText: this.proc,
                destination: 'serialized',
                // sourceFileName: sourceFile,
                sourceText: xmlString,
                stylesheetParams: xsltParameter,
                // logLevel: 10,
            });
            xmlOutput = result.principalResult;
        } catch (error) {
            return response.status(500).json({
                message: 'An error occurred while creating the user',
                error: error.message,
            });
        }

        response
            .header('Content-Type', 'application/xml; charset=utf-8')
            .header('Access-Control-Allow-Origin', '*')
            .header('Access-Control-Allow-Methods', 'GET,POST');
        response.status(StatusCodes.OK).send(xmlOutput);
    }

    protected handleRequest(oaiRequest: Dictionary, request: RequestContract) {
        // Setup stylesheet
        // $this->loadStyleSheet('datasetxml2oai-pmh.xslt');

        // Set response time
        const now: Dayjs = dayjs();
        this.xsltParameter['responseDate'] = now.format('YYYY-MM-DDTHH:mm:ss[Z]');
        this.xsltParameter['unixTimestamp'] = now.unix();

        // set OAI base url
        const baseDomain = process.env.BASE_DOMAIN || 'localhost';
        this.xsltParameter['baseURL'] = baseDomain + '/oai';
        this.xsltParameter['repURL'] = request.protocol() + '://' + request.hostname();
        this.xsltParameter['downloadLink'] = request.protocol() + '://' + request.hostname() + '/file/download/';
        this.xsltParameter['doiLink'] = 'https://doi.org/';
        this.xsltParameter['doiPrefix'] = 'info:eu-repo/semantics/altIdentifier/doi/';

        if (oaiRequest['verb']) {
            const verb = oaiRequest['verb'];
            this.xsltParameter['oai_verb'] = verb;
            if (verb === 'Identify') {
                this.handleIdentify();
            } else if (verb === 'ListMetadataFormats') {
                this.handleListMetadataFormats();
            }
            // else if (verb == "GetRecord") {
            //     await this.handleGetRecord(oaiRequest);
            // } else if (verb == "ListRecords") {
            //     await this.handleListRecords(oaiRequest);
            // } else if (verb == "ListIdentifiers") {
            //     await this.handleListIdentifiers(oaiRequest);
            // } else if (verb == "ListSets") {
            //     await this.handleListSets();
            // }
            else {
                this.handleIllegalVerb();
            }
        } else {
            // // try {
            // //     console.log("Async code example.")
            // const err = new PageNotFoundException("verb not found");
            // throw err;
            // // } catch (error) { // manually catching
            // //     next(error); // passing to default middleware error handler
            // // }
            throw new OaiModelException(
                StatusCodes.INTERNAL_SERVER_ERROR,
                'The verb provided in the request is illegal.',
                OaiErrorCodes.BADVERB,
            );
        }
    }

    protected handleIdentify() {
        const email = process.env.OAI_EMAIL || 'repository@geosphere.at';
        const repositoryName = 'Tethys RDR';
        const repIdentifier = 'tethys.at';
        const sampleIdentifier = 'oai:' + repIdentifier + ':1'; //$this->_configuration->getSampleIdentifier();

        // Dataset::earliestPublicationDate()->server_date_published->format('Y-m-d\TH:i:s\Z') : null;
        // earliestDateFromDb!= null && (this.xsltParameter['earliestDatestamp'] = earliestDateFromDb?.server_date_published);

        // set parameters for oai-pmh.xslt
        this.xsltParameter['email'] = email;
        this.xsltParameter['repositoryName'] = repositoryName;
        this.xsltParameter['repIdentifier'] = repIdentifier;
        this.xsltParameter['sampleIdentifier'] = sampleIdentifier;
        // $this->proc->setParameter('', 'earliestDatestamp', $earliestDateFromDb);

        this.xml.root().ele('Datasets');
    }

    protected handleListMetadataFormats() {
        this.xml.root().ele('Datasets');
    }

    private handleIllegalVerb() {
        this.xsltParameter['oai_error_code'] = 'badVerb';
        this.xsltParameter['oai_error_message'] = 'The verb provided in the request is illegal.';
    }
}
