import {Metrics} from '@interopio/gateway-metrics-api';
import {Middleware, WebExchange} from '../server/types.js';
import getLogger from '../logger.js';
import {FilePublisherConfig} from '@interopio/gateway/metrics/publisher/file';
import {HttpServerRequest, HttpServerResponse} from '../server/exchange.ts';

const logger = getLogger('metrics');

const COOKIE_NAME = 'GW_LOGIN';

function loggedIn(auth, ctx: WebExchange) {
    if (auth) {
        const cookieHeaderValue = ctx.request.headers.list('cookie');
        const cookie = cookieHeaderValue?.join('; ').split('; ').find(value => value.startsWith(`${COOKIE_NAME}=`));
        return cookie && (parseInt(cookie?.substring(COOKIE_NAME.length + 1)) > Date.now());
    }
    return true;
}

async function routes(config: {file?: FilePublisherConfig, auth?}): Promise<Middleware<HttpServerRequest, HttpServerResponse>> {
    const {jsonFileAppender} = await import('@interopio/gateway/metrics/publisher/file');
    const appender = jsonFileAppender(logger);
    await appender.open(config.file?.location ?? 'metrics.ndjson', config.file?.append ?? true);
    return [
        async (ctx, next: () => Promise<void>) => {
            if (ctx.method === 'GET' && ctx.path === '/api/metrics') {
                if (loggedIn(config.auth, ctx)) {
                    ctx.response.statusCode = 200;
                } else {
                    ctx.response.statusCode = 302;
                    ctx.response.headers.set('location', '/api/login?redirectTo=/api/metrics');
                }
                ctx.response._res.end();
            }
            else {
                await next();
            }
        },
        async (ctx, next: () => Promise<void>) => {
            if (ctx.method === 'GET' && ctx.path === '/api/login') {
                const redirectTo = new URLSearchParams(ctx.request.query ?? undefined).get('redirectTo');
                const expires = Date.now() + 180 * 1000;
                ctx.response.headers.add('set-cookie', `${COOKIE_NAME}=${expires}; Path=/api; SameSite=strict`)
                if (redirectTo) {
                    ctx.response.statusCode = 302;
                    ctx.response.headers.set('location', redirectTo);
                }
                else {
                    ctx.response.statusCode = 200;
                }
                ctx.response._res.end();
            } else {
                await next();
            }
        },
        async (ctx, next: () => Promise<void>) => {
            if (ctx.method === 'POST' && ctx.path === '/api/metrics') {
                if (loggedIn(config.auth, ctx)) {
                    ctx.response.statusCode = 202;
                    ctx.response._res.end();
                    try {
                        const json = await ctx.request.json;
                        const update = json as Metrics.Update;
                        if (logger.enabledFor('debug')) {
                            logger.debug(`${JSON.stringify(update)}`);
                        }
                        if ((config.file?.status ?? false) || (update.status === undefined)) {
                            await appender.write(update);
                        }
                    } catch (e) {
                        logger.error(`error processing metrics`, e);
                    }
                }
                else {
                    ctx.response.statusCode = 401;
                    ctx.response._res.end();
                }
            }
            else {
                await next();
            }
        },
    ];
}

export default routes;
