import { fn } from '@vitest/spy';
import { setTimeout } from 'node:timers/promises';
import { select as d3_select } from 'd3-selection';
import { dispatch as d3_dispatch } from 'd3-dispatch';

describe('iD.svgData', function () {
    var context;
    var surface;
    var dispatch = d3_dispatch('change');
    var projection = iD.geoRawMercator()
        .translate([6934098.868981334, 4092682.5519805425])
        .scale(iD.geoZoomToScale(17))
        .clipExtent([[0, 0], [1000, 1000]]);

    var geojson =
        '{' +
        '  "type": "FeatureCollection",' +
        '  "features": [' +
        '    {' +
        '      "type": "Feature",' +
        '      "geometry": {' +
        '        "type": "Point",' +
        '        "coordinates": [-74.38928604125977, 40.150275473401365]' +
        '      },' +
        '      "properties": {' +
        '        "abbr": "N.J.",' +
        '        "area": 19717.8,' +
        '        "name": "New Jersey",' +
        '        "name_en": "New Jersey",' +
        '        "osm_id": 316973311,' +
        '        "flag": true,' +
        '        "list": [1,2,3],' +
        '        "null": null,' +
        '        "object": {}' +
        '      },' +
        '      "id": 316973311' +
        '    }' +
        '  ]' +
        '}';

    var gj = JSON.parse(geojson);

    var gpx =
        '<?xml version="1.0"?>' +
        '<gpx version="1.1" creator="GDAL 2.2.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ogr="http://osgeo.org/gdal" xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">' +
        '<metadata><bounds minlat="40.150275473401365" minlon="-74.389286041259766" maxlat="40.150275473401365" maxlon="-74.389286041259766"/></metadata>' +
        '<wpt lat="40.150275473401365" lon="-74.389286041259766">' +
        '  <name>New Jersey</name>' +
        '  <extensions>' +
        '    <ogr:abbr>N.J.</ogr:abbr>' +
        '    <ogr:area>19717.8</ogr:area>' +
        '    <ogr:name_en>New Jersey</ogr:name_en>' +
        '    <ogr:osm_id>316973311</ogr:osm_id>' +
        '  </extensions>' +
        '</wpt>' +
        '</gpx>';

    var kml =
        '<?xml version="1.0" encoding="utf-8" ?>' +
        '<kml xmlns="http://www.opengis.net/kml/2.2">' +
        '<Document id="root_doc">' +
        '<Schema name="gpxtest" id="gpxtest">' +
        '    <SimpleField name="abbr" type="string"></SimpleField>' +
        '    <SimpleField name="area" type="float"></SimpleField>' +
        '    <SimpleField name="name_en" type="string"></SimpleField>' +
        '    <SimpleField name="osm_id" type="int"></SimpleField>' +
        '</Schema>' +
        '<Folder><name>gpxtest</name>' +
        '  <Placemark>' +
        '    <name>New Jersey</name>' +
        '    <ExtendedData><SchemaData schemaUrl="#gpxtest">' +
        '        <SimpleData name="abbr">N.J.</SimpleData>' +
        '        <SimpleData name="area">19717.8</SimpleData>' +
        '        <SimpleData name="name_en">New Jersey</SimpleData>' +
        '        <SimpleData name="osm_id">316973311</SimpleData>' +
        '    </SchemaData></ExtendedData>' +
        '      <Point><coordinates>-74.3892860412598,40.1502754734014</coordinates></Point>' +
        '  </Placemark>' +
        '</Folder>' +
        '</Document>' +
        '</kml>';

    function makeFile(contents, fileName, mimeType) {
        var blob = new Blob([contents], { type: mimeType });
        blob.lastModifiedDate = new Date();
        blob.name = fileName;
        return blob;
    }

    beforeEach(function () {
        context = iD.coreContext().assetPath('../dist/').init();
        d3_select(document.createElement('div'))
            .attr('class', 'main-map')
            .call(context.map().centerZoom([-74.389286, 40.1502754], 17));

        surface = context.surface();
    });


    it('creates layer-mapdata', function () {
        var render = iD.svgData(projection, context, dispatch).geojson(gj);
        surface.call(render);

        var layers = surface.selectAll('g.layer-mapdata').nodes();
        expect(layers.length).toEqual(1);
    });

    it('draws geojson', function () {
        var render = iD.svgData(projection, context, dispatch).geojson(gj);
        surface.call(render);

        var path;
        path = surface.selectAll('path.shadow');
        expect(path.nodes().length).toEqual(1);
        expect(path.attr('d')).to.match(/^M.*z$/);
        path = surface.selectAll('path.stroke');
        expect(path.nodes().length).toEqual(1);
        expect(path.attr('d')).to.match(/^M.*z$/);
    });

    describe('#fileList', function() {
        it('handles gpx files', async () => {
            var files = [ makeFile(gpx, 'test.gpx', 'application/gpx+xml') ];
            var render = iD.svgData(projection, context, dispatch);
            const spy = fn();
            dispatch.on('change', spy);
            render.fileList(files);

            await setTimeout(200);
            expect(spy).toHaveBeenCalledOnce();
            surface.call(render);
            var path;
            path = surface.selectAll('path.shadow');
            expect(path.nodes().length).toEqual(1);
            expect(path.attr('d')).to.match(/^M.*z$/);
            path = surface.selectAll('path.stroke');
            expect(path.nodes().length).toEqual(1);
            expect(path.attr('d')).to.match(/^M.*z$/);
        });

        it('handles kml files', async () => {
            var files = [ makeFile(kml, 'test.kml', 'application/vnd.google-earth.kml+xml') ];
            var render = iD.svgData(projection, context, dispatch);
            const spy = fn();
            dispatch.on('change', spy);
            render.fileList(files);

            await setTimeout(200);
            expect(spy).toHaveBeenCalledOnce();
            surface.call(render);
            var path;
            path = surface.selectAll('path.shadow');
            expect(path.nodes().length).toEqual(1);
            expect(path.attr('d')).to.match(/^M.*z$/);
            path = surface.selectAll('path.stroke');
            expect(path.nodes().length).toEqual(1);
            expect(path.attr('d')).to.match(/^M.*z$/);
        });

        it('handles geojson files', async () => {
            var files = [ makeFile(geojson, 'test.geojson', 'application/vnd.geo+json') ];
            var render = iD.svgData(projection, context, dispatch);
            const spy = fn();
            dispatch.on('change', spy);
            render.fileList(files);

            await setTimeout(200);
            expect(spy).toHaveBeenCalledOnce();
            surface.call(render);
            var path;
            path = surface.selectAll('path.shadow');
            expect(path.nodes().length).toEqual(1);
            expect(path.attr('d')).to.match(/^M.*z$/);
            path = surface.selectAll('path.stroke');
            expect(path.nodes().length).toEqual(1);
            expect(path.attr('d')).to.match(/^M.*z$/);
            expect(render.geojson().features[0].properties.osm_id).toBeTypeOf('string');
            expect(render.geojson().features[0].properties.flag).toBeTypeOf('string');
            expect(render.geojson().features[0].properties.list).toBeTypeOf('string');
            expect(render.geojson().features[0].properties.null).toBeTypeOf('string');
            expect(render.geojson().features[0].properties.object).toBeTypeOf('string');
        });
    });


    describe('#showLabels', function() {
        it('shows labels by default', function () {
            var render = iD.svgData(projection, context, dispatch).geojson(gj);
            surface.call(render);

            var label = surface.selectAll('text.label');
            expect(label.nodes().length).toEqual(1);
            expect(label.text()).toEqual('New Jersey');

            var halo = surface.selectAll('text.label-halo');
            expect(halo.nodes().length).toEqual(1);
            expect(halo.text()).toEqual('New Jersey');
        });


        it('hides labels with showLabels(false)', function () {
            var render = iD.svgData(projection, context, dispatch).geojson(gj).showLabels(false);
            surface.call(render);

            expect(surface.selectAll('text.label').empty()).toBeTruthy();
            expect(surface.selectAll('text.label-halo').empty()).toBeTruthy();
        });
    });

});
