import { createHash } from 'node:crypto';

import Sphericalmercator from '@mapbox/sphericalmercator';

import {
    config, logger, pgClients,
} from '@opengis/fastify-table/utils.js';

import mapnik from '../../../plugins/mapnik/funcs/mapnik.js';

const { RenderTile } = mapnik();

const mercator = new Sphericalmercator({ size: 256 });

/**
 * Формування растрового tile cartoCss
 *
 * @method GET
 * @alias rtile
 * @param {String} bbox - bbox
 * @param {Number} height - висота по координатам
 * @param {Number} width - ширина по координатам
 * @param {String} data - стилізація
 * @param {String} lang - мова
 * @param {String} z - координата z
 * @param {String} x - координата y
 * @param {String} y - координата x
 */

export default async function rtile({
    pg = pgClients.client, params, query, user,
}, reply) {
    if (!RenderTile) {
        return reply.status(400).send({ error: 'mapnik server address needed', code: 400 });
    }

    const { id, z, y } = params;

    const x = params.x.split('.')[0] - 0;

    if (!id) {
        return reply.status(400).send({ error: 'not enough params: id', code: 400 });
    }

    if (!x || !y || !z) {
        return reply.status(400).send({ error: 'not enough params: xyz', code: 400 });
    }

    const decodedPath = Buffer.from(id, 'base64url').toString('utf-8');

    const data = pg.pk?.['gis.rasters']
        ? await pg.queryCache(`select raster_id as id, source_path, is_public, 'raster' as type from gis.rasters where raster_id=$1::text or source_path=$2::text`, { args: [id, decodedPath], table: 'gis.rasters' })
            .then(el => el.rows?.[0] || {})
        : {};

    const carto = pg.pk?.['gis.cartocss']
        ? await pg.queryCache('select cartocss_id, source_path, is_public, is_static from gis.cartocss where cartocss_id=$1::text', { args: [id], table: 'gis.cartocss' }).then(el => el.rows?.[0] || {})
        : {};

    if (carto.cartocss_id) {
        Object.assign(data, {
            id: carto.cartocss_id,
            source_path: carto.source_path,
            is_public: carto.is_public,
            is_static: carto.is_static,
            type: 'css',
        });
    }

    if (!user?.uid && data.is_public === false) {
        return reply.status(403).send({ error: 'access restricted', code: 403 });
    }

    if (!data.id && data.type !== 'css') {
        Object.assign(data, { source_path: decodedPath });
    }

    if (!data.source_path && data.type !== 'css') {
        return reply.status(404).send({ error: 'raster / cartocss not found', code: 404 });
    }

    const bbox = mercator.bbox(y, x, z, false, '900913');

    try {
        const ttl = (query.nocache ? '0' : null)
            || (data.type === 'css' && !data.is_static ? '1w' : null);

        const md5 = data.source_path
            ? createHash('md5').update(data.source_path).digest('hex')
            : undefined;
        const base64 = data.source_path
            ? Buffer.from(data.source_path).toString('base64url')
            : undefined;

        const param = {
            path: data.type === 'css' ? null : data.source_path, // for rasters only
            name: data.type === 'css' && data.source_path ? `vector/${data.source_path}` : id, // required for cartocss
            width: 256,
            bbox,
            ttl,
            debug: query.debug,
        };
        // console.log(param);
        const result = await RenderTile(param);

        if (query.debug) {
            return {
                ...result,
                ttl,
                md5_path: md5,
                base64_path: base64,
                raster: !!data.id,
                carto: data.type === 'css',
                is_static: data.type === 'css' ? data.is_static : true,
            };
        }

        if (result.err) {
            logger.file('rtile/error', {
                error: result.err, x, y, z, id: data.id, type: data.type,
            });
            return reply.status(500).send({ error: config.local ? result.err : 'render error', code: 500 });
        }

        const buffer = Buffer.from(result.base64, 'base64');

        return reply.headers({ 'Content-Type': 'image/png', 'Cache-Control': query.nocache || query.nottl ? 'no-store, no-cache, must-revalidate' : 'public, max-age=2592000' }).send(buffer);
    }
    catch (err) {
        logger.file('rtile/error', {
            error: err.toString(), stack: err.stack, x, y, z, id: data.id, type: data.type,
        });
        return reply.status(500).send({ error: config.local ? err.toString() : 'rtile error', code: 500 });
    }
}
