import {
  getMeta, getTemplate, pgClients, getTemplates, yml2json,
} from '@opengis/fastify-table/utils.js';

const table = 'gis.maps';

const columnType = {
  text: 'text',
  date: 'date',
  bool: 'yes/no',
  numeric: 'number',
  integer: 'number',
  'timestamp without time zone': 'date',
  'timestamp with time zone': 'date',
};

export default async function getMap({ params = {}, pg = pgClients.client }, reply) {
  const { columns = [] } = await getMeta({ pg, table }) || {};

  const fields = columns.map(({ name, dataTypeID, title }) => ({
    name,
    type: columnType[pg.pgType?.[dataTypeID] || 'text'],
    label: title || name
  }));

  const mapList = getTemplates('map').map(el => el[0]);

  const rows = pg.pk?.['gis.maps'] ? await pg.query(
    `SELECT map_id as id, * FROM gis.maps where ${params.id ? '$1 in (map_id,map_key)' : '1=1'}`,
    [params.id].filter(Boolean),
  ).then(el => el.rows || []) : [];

  if (params.id && !mapList?.includes(params.id) && !rows[0]) {
    return reply.status(404).send({ status: 404, message: 'map not found' });
  }

  const totals = pg.queryCache ? await pg.queryCache('select json_object_agg(oid::regclass, reltuples) from pg_class')
    .then(el => el.rows?.[0]?.json_object_agg || {}) : {};

  const maps = pg.pk?.['gis.maps'] ? await pg.query(
    'SELECT map_id as id, map_key as slug, name FROM gis.maps where is_active and is_public order by name',
  ).then(el => el.rows || []) : [];

  if (params.id) {
    const map = rows[0] || {};
    const loadTemplate = await getTemplate('map', params.id) || await getTemplate('map', map.map_key);
    const obj = loadTemplate || map;

    const rawIds = [
      ...(Array.isArray(obj?.layers) ? obj.layers : []),
      ...(Array.isArray(obj?.ogc) ? obj.ogc : []),
      ...(Array.isArray(obj?.cartocss) ? obj.cartocss : []),
      ...(Array.isArray(obj?.rasters) ? obj.rasters : [])
    ];
    const layerIds = [...new Set(rawIds.map(id => id?.toString().trim()).filter(Boolean))];

    let layers = [];

    if (layerIds.length > 0) {
      const [resServices, resOgc, resRasters, resCarto] = await Promise.all([
        pg.query(`SELECT service_id::text as id, *, st_asgeojson(bbox)::json as bbox, st_asgeojson(center)::json as center FROM gis.services WHERE is_active = true AND is_public = true AND service_id::text = ANY($1)`, [layerIds]),
        pg.query(`SELECT ogc_service_id::text as id, url as service_url, * FROM gis.ogc_service WHERE enabled = true AND ispublic = true AND ogc_service_id::text = ANY($1)`, [layerIds]),
        pg.query(`SELECT raster_id::text as id, *, st_asgeojson(bbox)::json as bbox, st_asgeojson(center)::json as center FROM gis.rasters WHERE is_active = true AND is_public = true AND isadmin = false AND raster_id::text = ANY($1)`, [layerIds]),
        pg.query(`SELECT cartocss_id::text as id, *, st_asgeojson(geom)::json as bbox FROM gis.cartocss WHERE enabled = true AND is_public = true AND cartocss_id::text = ANY($1)`, [layerIds])
      ]);

      const allFetchedLayers = [
        ...resServices.rows.map(row => ({ ...row, type: row.service_type || 'layer' })),
        ...resOgc.rows.map(row => ({ ...row, type: 'ogc', source_path: row.table_name })),
        ...resRasters.rows.map(row => ({ ...row, type: 'raster' })),
        ...resCarto.rows.map(row => ({ ...row, type: 'cartocss' }))
      ];

      const dbLayersMap = new Map(allFetchedLayers.map(l => [String(l.id), l]));

      const layersResults = await Promise.all(layerIds.map(async (id) => {
        const idStr = String(id);

        if (!dbLayersMap.has(idStr)) {
          return null;
        }

        const dbData = dbLayersMap.get(idStr);
        const templateData = await getTemplate('layer', idStr);

        const layerData = { ...dbData, ...(templateData || {}) };

        layerData.count = totals[layerData.source_path || ''] || 0;
        layerData.visible = true;

        if (layerData.style && typeof layerData.style === 'string') {
          if (layerData.type === 'cartocss') {
            layerData.style = layerData.style.replace(/\s+/g, ' ').trim();
          } else if (layerData.type !== 'ogc') {
            layerData.style = yml2json(layerData.style);
          }
        }

        return layerData;
      }));

      layers = layersResults.filter(Boolean);
    }

    return reply.status(200).send({ maps, ...obj, layers });
  }

  if (params.id && !rows.length) {
    return reply.status(404).send('map not found');
  }

  return reply.status(200).send(params.id ? { ...rows[0], fields } : { rows, fields });
}