"""Main function for MVCM."""

import argparse
import logging
import os
from datetime import datetime as dt

import numpy as np
import psutil
import xarray as xr
from netCDF4 import Dataset  # type: ignore
from pkg_resources import get_distribution  # type: ignore
from rich.logging import RichHandler
from ruamel.yaml import YAML

import mvcm.read_data as rd
from mvcm.constants import SensorConstants
from mvcm.write_output import run_mvcm, save_output

_LOG_FORMAT = "%(message)s"
logging.basicConfig(level="NOTSET", datefmt="[%X]", format=_LOG_FORMAT, handlers=[RichHandler()])
logger = logging.getLogger(__name__)
LOG_LEVELS = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"]

proc = psutil.Process(os.getpid())
_VERSION = "0.0.0"


def main(
    satellite: str = "snpp",
    sensor: str = "viirs",
    data_path: str = "",
    mod02: str = "",
    mod03: str = "",
    img02: str | None = "",
    img03: str | None = "",
    threshold_file: str = "",
    geos_atm_1: str = "",
    geos_atm_2: str = "",
    geos_land: str = "",
    geos_ocean: str = "",
    geos_constants: str = "",
    ndvi_file: str = "",
    sst_file: str = "",
    eco_file: str = "",
    out_file: str = "",
    exe_path: str = "/home/pveglio/mvcm/mvcm",
    verbose: int = 0,
    *,
    debug: bool = False,
    hires_only: bool = False,
    mvcm_threshold_file: str | None,
) -> None:
    """MVCM main function.

    Parameters
    ----------
    satellite: str
        satellite name, not case-sensitive. Available options: [snpp, ]
    sensor: str
        sensor name, not case-sensitive. Available options: [viirs, ]
    data_path: str
        path where the data is stored
    mod02: str
        VIIRS MOD02 file name
    mod03: str
        VIIRS MOD03 file name
    img02: [optional] str
        VIIRS IMG02 file name
    img03: [optional] str
        VIIRS IMG03 file name
    threshold_file: str
        thresholds file name
    geos_atm_1: str
        file name of first GEOS5 file for atmospheric parameters
    goes_atm_2: str
        file name of second GEOS5 file for atmospheric parameters
    geos_land: str
        file name of GEOS5 land parameters
    geos_ocean: str
        file name of GEOS5 ocean parameters
    geos_constants: str
        file name of GEOS5 constants
    ndvi_file: str
        NDVI file name
    sst_file: str
        Sea surface temperature file name
    eco_file: str
        Ecosystem File
    out_file: str
        Output file name
    verbose: int
        Verbosity level
    exe_path: str
        Path to the MVCM executable
    debug: bool
        Debug mode
    hires_only: bool
        Only run MVCM with high resolution

    Returns
    -------
    None
    """
    verbose_level = np.minimum(verbose + 1, 4)
    logger.setLevel(LOG_LEVELS[verbose_level])

    if img02 is None or img03 is None:
        use_hires = False
    else:
        use_hires = True

    file_names = {
        "MOD02": f"{mod02}",
        "MOD03": f"{mod03}",
        "IMG02": f"{img02}",
        "IMG03": f"{img03}",
        "GEOS_atm_1": f"{geos_atm_1}",
        "GEOS_atm_2": f"{geos_atm_2}",
        "GEOS_land": f"{geos_land}",
        "GEOS_ocean": f"{geos_ocean}",
        "GEOS_constants": f"{geos_constants}",
        "NDVI": f"{ndvi_file}",
        "SST": f"{sst_file}",
        "ECO": f"{eco_file}",
        "ANC_DIR": f"{data_path}",
    }
    logger.info(f"Memory usage #1: {proc.memory_info().rss / 1e6} MB")

    if hires_only is False:
        logger.info("Running regular MVCM before high resolution")
        run_mvcm(file_names, mvcm_threshold_file, out_file, exe_path)

    with open(threshold_file) as f:
        text = f.read()
    thresholds = YAML(typ="safe").load(text)

    viirs_m_bands = SensorConstants.VIIRS_VIS_BANDS + SensorConstants.VIIRS_IR_BANDS

    with Dataset(file_names["MOD03"]) as f:
        attrs = {attr: f.getncattr(attr) for attr in f.ncattrs()}
        lat_shape = f["geolocation_data/latitude"][:].shape

    fmt_in = "%Y-%m-%dT%H:%M:%S.%fZ"
    fmt_out = "%Y-%m-%dT%H%M"

    # We are not processing night granules
    if use_hires is True:
        with xr.open_dataset(file_names["IMG02"], group="observation_data") as vnp02:
            for b in SensorConstants.VIIRS_IMG_BANDS:
                try:
                    vnp02[b]
                except KeyError:
                    logger.info(f"Band {b} not found in file. Hires data will be fill values.")
                    attrs = {"version": _VERSION, "satellite": "VIIRS", "sensor": "VIIRS"}
                    save_output(None, attrs, out_file, compression=5, debug=debug, shape=lat_shape)
                    return
            logger.info(f"All bands found in file {file_names['IMG02']}. The code will run.")
    else:
        with xr.open_dataset(file_names["MOD02"], group="observation_data") as vnp02:
            for b in viirs_m_bands:
                try:
                    vnp02[b]
                except KeyError:
                    logger.info(f"Band {b} not found in file. Hires data will be fill values.")
                    attrs = {"version": _VERSION, "satellite": "VIIRS", "sensor": "VIIRS"}
                    save_output(None, attrs, out_file, compression=5, debug=debug, shape=lat_shape)
                    return
            logger.info(f"All bands found in file {file_names['MOD02']}. The code will run.")

    logger.info(f"Memory usage #2: {proc.memory_info().rss / 1e6} MB")
    # rd.get_data(satellite, sensor, file_names, thresholds, hires=use_hires)
    viirs_data, pixel_type = rd.get_data(satellite, sensor, file_names, thresholds, hires=use_hires)
    # viirs_data = rd.get_data(satellite, sensor, file_names, thresholds, hires=use_hires)

    # viirs_data = xr.open_dataset("input_data.nc")
    logger.info(f"Memory usage #3: {proc.memory_info().rss / 1e6} MB")

    with xr.open_dataset(file_names["MOD02"]) as vnp02:
        time_str = dt.strftime(dt.strptime(vnp02.time_coverage_start, fmt_in), fmt_out)

    viirs_data.to_netcdf(
        f"{os.path.dirname(out_file)}/input_data_{time_str}.nc", mode="w", format="NETCDF4"
    )
    pixel_type.to_netcdf(
        f"{os.path.dirname(out_file)}/pixel_type_{time_str}.nc", mode="w", format="NETCDF4"
    )

    logger.info(f"Memory usage #4: {proc.memory_info().rss / 1e6} MB")
    ###


if __name__ == "__main__":
    _VERSION = get_distribution("mvcm").version
    parser = argparse.ArgumentParser(prog="MVCM", description="")

    parser.add_argument(
        "--satellite",
        help="satellite name, not case-sensitive. Available options: [snpp, ]",
        choices=[
            "snpp",
        ],
    )
    parser.add_argument(
        "--sensor",
        help="sensor name, not case-sensitive. Available options: [viirs, ]",
        choices=[
            "viirs",
        ],
    )
    parser.add_argument("--path", help="path where the data is stored")
    parser.add_argument("--l1b", help="level 1b file name")
    parser.add_argument("--geolocation", help="geolocation file name")
    parser.add_argument("--hires-l1b", help="VIIRS IMG02 file name")
    parser.add_argument("--hires-geo", help="VIIRS IMG03 file name")
    parser.add_argument("-t", "--threshold", help="thresholds file name")
    parser.add_argument(
        "--atmos-1",
        help="file name of the first GEOS-IT file for atmospheric parameters",
    )
    parser.add_argument(
        "--atmos-2",
        help="file name of the second GEOS-IT file for atmospheric parameters",
    )
    parser.add_argument("--land", help="file name of GEOS-IT land parameters")
    parser.add_argument("--ocean", help="file name of GEOS-IT ocean parameters")
    parser.add_argument("--constants", help="file name of GEOS-IT constants")
    parser.add_argument("--ndvi", help="NDVI file name")
    parser.add_argument("--sst", help="Sea surface temperature file name")
    parser.add_argument("--eco", help="Ecosystem file")
    parser.add_argument("-o", "--out", help="output file name")
    parser.add_argument(
        "-V",
        "--version",
        action="version",
        version=_VERSION,
        help="print version and exit",
    )
    parser.add_argument(
        "-v", "--verbose", action="count", default=1, help="print verbose information"
    )
    parser.add_argument("-d", "--debug", action="store_true", help="activate debug mode")
    parser.add_argument("--hires-only", action="store_true", help="run only high resolution code")
    parser.add_argument("-x", "--mvcm-exe-path", help="path to mvcm executable")
    parser.add_argument(
        "--mvcm-thresholds", help="thresholds file name for operational MVCM", default=None
    )

    args = parser.parse_args()

    satellite = args.satellite or "snpp"
    sensor = args.sensor or "viirs"
    data_path = args.path
    mod02 = args.l1b
    mod03 = args.geolocation
    img02 = args.hires_l1b or None
    img03 = args.hires_geo or None
    threshold_file = args.threshold
    geos_atm_1 = args.atmos_1
    geos_atm_2 = args.atmos_2
    geos_land = args.land
    geos_ocean = args.ocean
    constants = args.constants
    ndvi_file = args.ndvi
    sst_file = args.sst
    eco_file = args.eco
    out_file = args.out
    verbose = args.verbose or False
    debug = args.debug or False

    main(
        satellite=satellite,
        sensor=sensor,
        data_path=data_path,
        mod02=mod02,
        mod03=mod03,
        img02=img02,
        img03=img03,
        threshold_file=threshold_file,
        geos_atm_1=geos_atm_1,
        geos_atm_2=geos_atm_2,
        geos_land=geos_land,
        geos_ocean=geos_ocean,
        geos_constants=constants,
        ndvi_file=ndvi_file,
        sst_file=sst_file,
        eco_file=eco_file,
        out_file=out_file,
        verbose=verbose,
        debug=debug,
        exe_path=args.mvcm_exe_path,
        hires_only=args.hires_only,
        mvcm_threshold_file=args.mvcm_thresholds,
    )

# tracemalloc.stop()