"""Entrypoint for the `ome_zarr` command-line tool."""

import argparse
import logging
import sys

from .csv import csv_to_zarr
from .data import astronaut, coins, create_zarr
from .format import CurrentFormat, Format, format_from_version
from .utils import download as zarr_download
from .utils import finder as bff_finder
from .utils import info as zarr_info
from .utils import view as zarr_view


def config_logging(loglevel: int, args: argparse.Namespace) -> None:
    """Configure logging taking the `verbose` and `quiet` arguments into account.

    Each `-v` increases the `loglevel` by 10 and each `-q` reduces the loglevel by 10.
    For example, an initial loglevel of `INFO` will be converted to `DEBUG` via `-qqv`.
    """
    loglevel = loglevel - (10 * args.verbose) + (10 * args.quiet)
    logging.basicConfig(level=loglevel)
    # DEBUG logging for s3fs so we can track remote calls
    logging.getLogger("s3fs").setLevel(logging.DEBUG)


def info(args: argparse.Namespace) -> None:
    """Wrap the :func:`~ome_zarr.utils.info` method."""
    config_logging(logging.WARNING, args)
    list(zarr_info(args.path, stats=args.stats))


def view(args: argparse.Namespace) -> None:
    """Wrap the :func:`~ome_zarr.utils.view` method."""
    config_logging(logging.WARNING, args)
    zarr_view(args.path, args.port, force=args.force)


def finder(args: argparse.Namespace) -> None:
    """Wrap the :func:`~ome_zarr.utils.finder` method."""
    config_logging(logging.WARNING, args)
    bff_finder(args.path, args.port)


def download(args: argparse.Namespace) -> None:
    """Wrap the :func:`~ome_zarr.utils.download` method."""
    config_logging(logging.WARNING, args)
    zarr_download(args.path, args.output)


def create(args: argparse.Namespace) -> None:
    """Chooses between data generation methods in :mod:`ome_zarr.utils` like.

    :func:`~ome_zarr.data.coins` or :func:`~ome_zarr.data.astronaut`.
    """
    config_logging(logging.WARNING, args)
    if args.method == "coins":
        method = coins
        label_name = "coins"
    elif args.method == "astronaut":
        method = astronaut
        label_name = "circles"
    else:
        raise Exception(f"unknown method: {args.method}")
    fmt: Format = CurrentFormat()
    if args.format:
        fmt = format_from_version(args.format)

    create_zarr(args.path, method=method, label_name=label_name, fmt=fmt)


def scale(args: argparse.Namespace) -> None:
    import dask.array as da
    import zarr

    from .writer import write_image

    """Wrap the :func:`~ome_zarr.scale._build_pyramid` method."""
    base = zarr.open_array(args.input_array, mode="r")
    scale_factors = tuple(args.downscale**i for i in range(1, args.max_layer + 1))

    data = da.from_zarr(args.input_array)

    write_image(
        data,
        args.output_directory,
        axes=str(args.axes),
        method=args.method,
        scale_factors=scale_factors,
    )

    grp = zarr.open_group(args.output_directory, mode="a")

    if args.copy_metadata:
        print(f"copying attribute keys: {list(base.attrs.keys())}")
        grp.attrs.update(base.attrs)


def csv_to_labels(args: argparse.Namespace) -> None:
    """Adds csv data to labels properties"""

    print("csv_to_labels", args.csv_path, args.zarr_path)

    csv_to_zarr(args.csv_path, args.csv_id, args.csv_keys, args.zarr_path, args.zarr_id)


def main(args: list[str] | None = None) -> None:
    """Run appropriate function with argparse arguments, handling errors."""
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-v",
        "--verbose",
        action="count",
        default=0,
        help="increase loglevel for each use, e.g. -vvv",
    )
    parser.add_argument(
        "-q",
        "--quiet",
        action="count",
        default=0,
        help="decrease loglevel for each use, e.q. -qqq",
    )
    subparsers = parser.add_subparsers(dest="command")
    subparsers.required = True

    # info
    parser_info = subparsers.add_parser("info")
    parser_info.add_argument("path", help="Path to image.zarr")
    parser_info.add_argument("--stats", action="store_true")
    parser_info.set_defaults(func=info)

    # download
    parser_download = subparsers.add_parser("download")
    parser_download.add_argument("path")
    parser_download.add_argument("--output", default=".")
    parser_download.set_defaults(func=download)

    # view (in ome-ngff-validator in a browser)
    parser_view = subparsers.add_parser("view")
    parser_view.add_argument(
        "path",
        help="Path to image.zarr to open in ome-ngff-validator",
    )
    parser_view.add_argument(
        "--port", type=int, default=8000, help="Port to serve the data (default: 8000)"
    )
    parser_view.add_argument(
        "--force",
        "-f",
        action="store_true",
        help="Force open in browser. Don't check for OME-Zarr data first.",
    )
    parser_view.set_defaults(func=view)

    # finder (open a dir of images in BioFile Finder in a browser)
    parser_finder = subparsers.add_parser("finder")
    parser_finder.add_argument(
        "path",
        help="Directory to open in BioFile Finder",
    )
    parser_finder.add_argument(
        "--port", type=int, default=8000, help="Port to serve the data (default: 8000)"
    )
    parser_finder.set_defaults(func=finder)

    # create
    parser_create = subparsers.add_parser("create")
    parser_create.add_argument(
        "--method", choices=("coins", "astronaut"), default="coins"
    )
    parser_create.add_argument("path")
    parser_create.add_argument(
        "--format", help="OME-Zarr version to create. e.g. '0.4'"
    )
    parser_create.set_defaults(func=create)

    parser_scale = subparsers.add_parser("scale")
    parser_scale.add_argument("input_array")
    parser_scale.add_argument("output_directory")
    parser_scale.add_argument(
        "axes", type=str, help="Dimensions of input data, i.e. 'zyx' or 'tczyx'."
    )
    parser_scale.add_argument(
        "--copy-metadata",
        action="store_true",
        help="copies the array metadata to the new group",
    )
    parser_scale.add_argument(
        "--method",
        choices=["nearest", "resize", "laplacian", "local_mean", "zoom"],
        default="resize",
    )
    parser_scale.add_argument(
        "--in-place", action="store_true", help="if true, don't write the base array"
    )
    parser_scale.add_argument("--downscale", type=int, default=2)
    parser_scale.add_argument("--max_layer", type=int, default=4)

    parser_scale.set_defaults(func=scale)

    # csv to label properties
    parser_csv_to_labels = subparsers.add_parser("csv_to_labels")
    parser_csv_to_labels.add_argument("csv_path", help="path to csv file")
    parser_csv_to_labels.add_argument(
        "csv_id",
        help="csv column name containing ID for identifying label properties to update",
    )
    parser_csv_to_labels.add_argument(
        "csv_keys", help="Comma-separated list of columns to read from csv to zarr"
    )
    parser_csv_to_labels.add_argument(
        "zarr_path", help="path to local zarr plate or image"
    )
    parser_csv_to_labels.add_argument(
        "zarr_id",
        help="Labels properties key. Values should match csv_id column values",
    )
    parser_csv_to_labels.set_defaults(func=csv_to_labels)

    ns = parser.parse_args(args)

    if args is None:
        ns = parser.parse_args(sys.argv[1:])
    else:
        ns = parser.parse_args(args)

    try:
        ns.func(ns)
    except AssertionError as error:
        logging.getLogger("ome_zarr.cli").error(error)
        sys.exit(2)
