"""fio-cat"""

import json
import warnings

import click
import cligj

import fiona
from fiona.transform import transform_geom
from fiona.model import Feature, ObjectEncoder
from fiona.fio import options, with_context_env
from fiona.fio.helpers import recursive_round
from fiona.errors import AttributeFilterError

warnings.simplefilter("default")


# Cat command
@click.command(short_help="Concatenate and print the features of datasets")
@click.argument("files", nargs=-1, required=True, metavar="INPUTS...")
@click.option(
    "--layer",
    default=None,
    multiple=True,
    callback=options.cb_multilayer,
    help="Input layer(s), specified as 'fileindex:layer` "
    "For example, '1:foo,2:bar' will concatenate layer foo "
    "from file 1 and layer bar from file 2",
)
@cligj.precision_opt
@cligj.indent_opt
@cligj.compact_opt
@click.option(
    "--ignore-errors/--no-ignore-errors",
    default=False,
    help="log errors but do not stop serialization.",
)
@options.dst_crs_opt
@cligj.use_rs_opt
@click.option(
    "--bbox",
    default=None,
    metavar="w,s,e,n",
    help="filter for features intersecting a bounding box",
)
@click.option(
    "--where",
    default=None,
    help="attribute filter using SQL where clause",
)
@click.option(
    "--cut-at-antimeridian",
    is_flag=True,
    default=False,
    help="Optionally cut geometries at the anti-meridian. To be used only for a geographic destination CRS.",
)
@click.pass_context
@with_context_env
def cat(
    ctx,
    files,
    precision,
    indent,
    compact,
    ignore_errors,
    dst_crs,
    use_rs,
    bbox,
    where,
    cut_at_antimeridian,
    layer,
):
    """
    Concatenate and print the features of input datasets as a sequence of
    GeoJSON features.

    When working with a multi-layer dataset the first layer is used by default.
    Use the '--layer' option to select a different layer.

    """
    dump_kwds = {"sort_keys": True}
    if indent:
        dump_kwds["indent"] = indent
    if compact:
        dump_kwds["separators"] = (",", ":")

    # Validate file idexes provided in --layer option
    # (can't pass the files to option callback)
    if layer:
        options.validate_multilayer_file_index(files, layer)

    # first layer is the default
    for i in range(1, len(files) + 1):
        if str(i) not in layer.keys():
            layer[str(i)] = [0]

    try:
        if bbox:
            try:
                bbox = tuple(map(float, bbox.split(",")))
            except ValueError:
                bbox = json.loads(bbox)

        for i, path in enumerate(files, 1):
            for lyr in layer[str(i)]:
                with fiona.open(path, layer=lyr) as src:
                    for i, feat in src.items(bbox=bbox, where=where):
                        geom = feat.geometry

                        if dst_crs:
                            geom = transform_geom(
                                src.crs,
                                dst_crs,
                                geom,
                                antimeridian_cutting=cut_at_antimeridian,
                            )

                        if precision >= 0:
                            geom = recursive_round(geom, precision)

                        feat = Feature(
                            id=feat.id,
                            properties=feat.properties,
                            geometry=geom,
                            bbox=fiona.bounds(geom),
                        )

                        if use_rs:
                            click.echo("\x1e", nl=False)

                        click.echo(json.dumps(feat, cls=ObjectEncoder, **dump_kwds))

    except AttributeFilterError as e:
        raise click.BadParameter("'where' clause is invalid: " + str(e))
