"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RevalidateHandler = void 0;
const console_1 = require("../lib/console");
const lodash_1 = require("lodash");
class RevalidateHandler {
    constructor(resourceService, renderService, s3Service, cloudfrontService) {
        this.resourceService = resourceService;
        this.renderService = renderService;
        this.s3Service = s3Service;
        this.cloudfrontService = cloudfrontService;
    }
    run(event, context, manifest, cacheControl) {
        return __awaiter(this, void 0, void 0, function* () {
            const resource = this.resourceService.get(event);
            console_1.debug(`[RevalidateHandler] 1: ${JSON.stringify(event)} || ${JSON.stringify(context)} || ${JSON.stringify(manifest)}`);
            console.log(JSON.stringify(resource));
            console_1.debug(JSON.stringify(event));
            console_1.debug(JSON.stringify(context));
            const [htmlHeader, jsonHeader, candidatePage] = yield Promise.all([
                this.s3Service.getHeader(resource.getHtmlKey()),
                this.s3Service.getHeader(resource.getJsonKey()),
                this.renderService.getPage(resource.getPagePath(), resource.getJsonUri())
            ]);
            const revalidateTriggerGapSecond = console_1.isDevMode() ? 1 : 300;
            if (RevalidateHandler.shouldSkipRevalidate(htmlHeader.header.LastModified, revalidateTriggerGapSecond)) {
                console_1.debug(`The last ISR was triggered ${revalidateTriggerGapSecond} seconds ago, so skip this one.`);
                return;
            }
            console_1.debug(`[handler] Revalidate resource: ${JSON.stringify(resource)}`);
            if (!candidatePage) {
                throw new Error(`Page for ${resource.getPagePath()} not found`);
            }
            console_1.debug(`Current HTML ETAG: ${htmlHeader.getETag()}, Candidate Page HTML ETAG: ${candidatePage.getHtmlEtag()}`);
            console_1.debug(`Current JSON ETAG: ${jsonHeader.getETag()}, Candidate Page JSON ETAG: ${candidatePage.getJsonEtag()}`);
            console_1.debug(`CANDIDATE PAGE: ${JSON.stringify(candidatePage)}`);
            if (RevalidateHandler.shouldRemoveResource(candidatePage)) {
                console_1.debug(`remove old resource for ${{
                    candidatePage: JSON.stringify(candidatePage)
                }}`);
                yield Promise.all([
                    this.s3Service.deleteObject(resource.getHtmlKey()),
                    this.s3Service.deleteObject(resource.getJsonKey())
                ]);
                yield this.createInvalidation(resource, manifest);
                return;
            }
            if ((yield this.isContentChanged(candidatePage, resource)) || console_1.isDevMode()) {
                console_1.debug(`[handler] isDevMode():${console_1.isDevMode()} or resource changed, update S3 cache and invalidate. html: ${resource.getHtmlKey()}, json:${resource.getJsonKey()}`);
                yield Promise.all([
                    this.s3Service.putObject(resource.getHtmlKey(), candidatePage.getHtmlBody(), "text/html", cacheControl),
                    this.s3Service.putObject(resource.getJsonKey(), candidatePage.getJsonBody(), "application/json")
                ]);
                yield this.createInvalidation(resource, manifest);
                return;
            }
            console_1.debug(`[handler] Resource is not changed.}`);
            return;
        });
    }
    static shouldRemoveResource(page) {
        var _a, _b, _c;
        const pageData = ((_a = page.getJson()) !== null && _a !== void 0 ? _a : {});
        const isRedirect = !lodash_1.isNil((_b = pageData.pageProps) === null || _b === void 0 ? void 0 : _b.__N_REDIRECT) &&
            !lodash_1.isEmpty((_c = pageData.pageProps) === null || _c === void 0 ? void 0 : _c.__N_REDIRECT);
        const isEmptyHtml = lodash_1.isEmpty(page.getHtmlBody());
        return isRedirect || isEmptyHtml;
    }
    static shouldSkipRevalidate(lastModified, gap) {
        if (lodash_1.isNil(lastModified))
            return false;
        console_1.debug(`[checkRevalidateTimeGap] lastModified at ${lastModified}, current: ${new Date()}`);
        return new Date() < new Date(lastModified.getTime() + gap * 1000);
    }
    isContentChanged(candidatePage, resource) {
        return __awaiter(this, void 0, void 0, function* () {
            console_1.debug(`[isContentChanged] resource json key: ${resource.getJsonKey()}`);
            const oldData = JSON.parse(yield this.s3Service.getObject(resource.getJsonKey()));
            const newData = candidatePage.getJson();
            const isDataChanged = !lodash_1.isEqual(lodash_1.omit(oldData.pageProps, "generatedAt"), lodash_1.omit(newData.pageProps, "generatedAt"));
            console_1.debug(`[isContentChanged] data compare ${JSON.stringify({ oldData, newData })}`);
            return isDataChanged;
        });
    }
    createInvalidation(resource, manifest) {
        return __awaiter(this, void 0, void 0, function* () {
            console_1.debug(`[createInvalidation]  createInvalidation for ${resource.getHtmlUri()}, ${resource.getJsonUri()}`);
            const useRemoteInvalidation = manifest.enableRemoteInvalidation;
            const env = console_1.getEnvironment(manifest);
            if (useRemoteInvalidation) {
                yield this.cloudfrontService.createRemoteInvalidation([resource.getHtmlUri(), resource.getJsonUri()], env);
            }
            else {
                yield this.cloudfrontService.createInvalidation([
                    resource.getHtmlUri(),
                    resource.getJsonUri()
                ]);
            }
        });
    }
}
exports.RevalidateHandler = RevalidateHandler;
