import carto from 'carto';

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

const { UploadXML } = mapnik();

const srsList = {};

const getGeomColumn = (geoms, customColumn) => {
    if (customColumn && geoms.includes(customColumn)) {
        return customColumn;
    }
    if (geoms.includes('geom')) {
        return 'geom';
    }
    return geoms[0];
};

import cartoBounds from './cartoBounds.js';

function cartoData(el, extent, pg) {
    const minzoom = el.zoom ? (+el.zoom || 5) : 5;
    const maxzoom = el.maxzoom ? (+el.maxzoom || 22) : 22;
    const view = `(select ${el.columns || '1'}, ${el.gcol} from ${el.table} where ${el.query || 'true'} ) as data`;
    const layerBound = extent || [-180, -85, 180, 85];

    return {
        id: el.caption,
        name: el.caption,
        properties: { minzoom, maxzoom },
        srs: el.proj4text || '+proj=longlat +datum=WGS84 +no_defs',
        Datasource: {
            simplify_geometries: true,
            type: 'postgis',
            extent: layerBound.join(','),
            table: view,
            geometry_field: el.gcol,
            host: pg.options?.host || 'localhost',
            dbname: pg.options?.database || 'postgres',
            port: pg.options?.port || 5432,
            user: pg.options?.user || 'postgres',
            password: pg.options?.password || 'postgres',
        },
    };
}

export default async function uploadXML({ id, dataset, style }, pg) {
    if (!id || !dataset?.length || Array.isArray(dataset) === false) {
        return null;
    }
    await Promise.all(dataset.filter((el) => el.active !== false).map(async el => {
        const { table } = el;

        const geoms = await pg.query(`SELECT json_agg(attname) as geoms FROM pg_attribute
      where attrelid = to_regclass($1::text)
      and attnum > 0
      AND NOT attisdropped
      and atttypid::regtype::text ='geometry'`, [table]).then(e => e.rows?.[0]?.geoms || []);

        const gcol = getGeomColumn(geoms, el.gcol);
        Object.assign(el, { gcol });

        const srid = el.srid ? el.srid
            : await pg.query(`select st_srid(${gcol}) as srid from ${table} where ${gcol} is not null limit 1`).then(e => e.rows[0]?.srid);

        if (srid && !srsList[srid] && (srid - 0 > 0)) {
            srsList[srid] = await pg.query(`SELECT proj4text from spatial_ref_sys where srid=$1`, [srid])
                .then(e => e.rows[0]?.proj4text);
        }
        Object.assign(el, { gcol, proj4text: srsList[srid] });
    }));

    const bounds = await cartoBounds({ id, dataset }, pg);

    const arrayLayers = dataset.filter((el) => el.active !== false).map((el) => cartoData(el, bounds, pg));

    const configJson = {
        format: 'png',
        Layer: arrayLayers.reverse(),
        srs: '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over',
        bounds,
        'maximum-extent': '-20037508.34,-20037508.34,20037508.34,20037508.34',
        center: [0, 0, 2],
        minzoom: 1,
        maxzoom: 25,
        Stylesheet: [{
            id: 'carto_css_style',
            data: style || '',
        }],
    };

    // generate
    const res = new carto.Renderer().render(configJson);
    const xmlFileText = typeof res === 'string' ? res : res?.data;
    if (!xmlFileText) {
        throw new Error('xml generation error');
    }
    const { status } = await UploadXML({
        name: id,
        xml: xmlFileText,
    });
    if (status !== 'success') {
        throw new Error('xml upload error');
    }
    return xmlFileText;
}
