'use strict';


const Stream = require('stream');

const Boom = require('@hapi/boom');


const internals = {};


exports.cache = function (response) {

    const request = response.request;
    if (response.headers['cache-control']) {
        return;
    }

    const settings = request.route.settings.cache;
    const policy = settings && request._route._cache && (settings._statuses.has(response.statusCode) || (response.statusCode === 304 && settings._statuses.has(200)));

    if (policy ||
        response.settings.ttl) {

        const ttl = response.settings.ttl !== null ? response.settings.ttl : request._route._cache.ttl();
        const privacy = request.auth.isAuthenticated || response.headers['set-cookie'] ? 'private' : settings.privacy ?? 'default';
        response._header('cache-control', 'max-age=' + Math.floor(ttl / 1000) + ', must-revalidate' + (privacy !== 'default' ? ', ' + privacy : ''));
    }
    else if (settings) {
        response._header('cache-control', settings.otherwise);
    }
};


exports.content = async function (response) {

    const request = response.request;
    if (response._isPayloadSupported() ||
        request.method === 'head') {

        await response._marshal();

        if (typeof response._payload.size === 'function') {
            response._header('content-length', response._payload.size(), { override: false });
        }

        if (!response._isPayloadSupported()) {
            response._close();                              // Close unused file streams
            response._payload = new internals.Empty();      // Set empty stream
        }

        exports.type(response);
    }
    else {

        // Set empty stream

        response._close();                                  // Close unused file streams
        response._payload = new internals.Empty();
        delete response.headers['content-length'];
    }
};


exports.state = async function (response) {

    const request = response.request;
    const states = [];

    for (const stateName in request._states) {
        states.push(request._states[stateName]);
    }

    try {
        for (const name in request._core.states.cookies) {
            const autoValue = request._core.states.cookies[name].autoValue;
            if (!autoValue || name in request._states || name in request.state) {
                continue;
            }

            if (typeof autoValue !== 'function') {
                states.push({ name, value: autoValue });
                continue;
            }

            const value = await autoValue(request);
            states.push({ name, value });
        }

        if (!states.length) {
            return;
        }

        let header = await request._core.states.format(states, request);
        const existing = response.headers['set-cookie'];
        if (existing) {
            header = (Array.isArray(existing) ? existing : [existing]).concat(header);
        }

        response._header('set-cookie', header);
    }
    catch (err) {
        const error = Boom.boomify(err);
        request._log(['state', 'response', 'error'], error);
        request._states = {};                                           // Clear broken state
        throw error;
    }
};


exports.type = function (response) {

    const type = response.contentType;
    if (type !== null && type !== response.headers['content-type']) {
        response.type(type);
    }
};


exports.entity = function (response) {

    const request = response.request;

    if (!request._entity) {
        return;
    }

    if (request._entity.etag &&
        !response.headers.etag) {

        response.etag(request._entity.etag, { vary: request._entity.vary });
    }

    if (request._entity.modified &&
        !response.headers['last-modified']) {

        response.header('last-modified', request._entity.modified);
    }
};


exports.unmodified = function (response) {

    const request = response.request;
    if (response.statusCode === 304) {
        return;
    }

    const entity = {
        etag: response.headers.etag,
        vary: response.settings.varyEtag,
        modified: response.headers['last-modified']
    };

    const etag = request._core.Response.unmodified(request, entity);
    if (etag) {
        response.code(304);

        if (etag !== true) {                                // Override etag with incoming weak match
            response.headers.etag = etag;
        }
    }
};


internals.Empty = class extends Stream.Readable {

    _read(/* size */) {

        this.push(null);
    }

    writeToStream(stream) {

        stream.end();
    }
};
