#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Application to print obsinfo information files.
"""
import os
import warnings
from pathlib import Path
from argparse import ArgumentParser

import logging
from logging.handlers import RotatingFileHandler

# obsinfo modules
import obsinfo
from obsinfo.obsMetadata.obsmetadata import ObsMetadata
from obsinfo.instrumentation import (Instrumentation, Stage, Filter,
                                     Preamplifier, Sensor, Datalogger)
from obsinfo.helpers import Location, Locations, Person
from obsinfo.subnetwork import (Subnetwork, Operator)
from obsinfo.misc.discoveryfiles import Datapath

warnings.simplefilter("once")
warnings.filterwarnings("ignore", category=DeprecationWarning)
verbose = False


def print_single_file(info_file, n_sublevels=0, configs=False, verbose=True):
    """
    Print a single obsinfo file.

    Args:
        info_file (str): info file to validate.
        n_sublevels (int): number of sublevels to print_directory
        configs (boolean): print configurations instead of object
    """
    filetype = ObsMetadata.get_information_file_type(info_file)
    try:
        dp = Datapath()
        attributes_dict = ObsMetadata().read_info_file(info_file, dp, quiet=True)
    except Exception as e:
        print(str(e))
        return False
    if configs is not True:
        print(f'{Path(info_file).name}: ', end='', flush=True)
        no_class_msg = f"Can't print {filetype} files (no associated class)"
        if filetype == 'subnetwork':
            obj = Subnetwork(attributes_dict[filetype])
        elif filetype == 'instrumentation_base':
            location_code='00'
            locations = Locations(
                [Location({'position': {'lat': 0, 'lon': 0, 'elev': 0},
                           'base': {'uncertainties.m': {'lat': 0, 'lon': 0, 'elev':0},
                                    'depth.m': 0,
                                    'geology': 'seafloor',
                                    'vault': 'seafloor'},
                           'code': '00'})
                ])
            obj = Instrumentation({'base': attributes_dict[filetype]},
                                  locations, location_code,
                                  '2000-01-01', '3000-01-01')
        elif filetype == 'datalogger_base':
            obj = Datalogger({'base': attributes_dict[filetype]})
        elif filetype == 'sensor_base':
            obj = Sensor({'base': attributes_dict[filetype]})
        elif filetype == 'preamplifier_base':
            obj = Preamplifier({'base': attributes_dict[filetype]})
        elif filetype == 'location_base':
            obj = Location({'base': attributes_dict[filetype]})
        elif filetype == 'stage_base':
            obj = Stage({'base': attributes_dict[filetype]})
        elif filetype == 'network':
            print(no_class_msg)
            return
        elif filetype == 'operator':
            obj = Operator(attributes_dict[filetype])
        elif filetype == 'filter':
            obj = Filter.construct(attributes_dict[filetype])
        elif filetype == 'person':
            obj = Person(attributes_dict[filetype])
        elif filetype == 'timing_base':
            print(no_class_msg)
            return
        else:
            print(f"Unknown information file type: '{filetype}")
        print("\n    " + obj.__str__(indent=4, n_subclasses=n_sublevels))
    else:
        if filetype in ('instrumentation_base', 'datalogger_base',
                        'sensor_base' 'preamplifier_base',
                        'location_base', 'stage_base', 'timing_base'):
            configs = attributes_dict[filetype].get('configurations', {})
            keys = list(configs.keys())
            if verbose or len(keys) > 0:
                print(f'{Path(info_file).name}: ', end='', flush=True)
                print(f"{list(configs.keys())}")
        else:
            if verbose:
                print(f'{Path(info_file).name}: ', end='', flush=True)
                print(f"file type: '{filetype}' has no configurations")
            return


def print_directory(info_dir, n_sublevels=False, configs=False,
                    drilldown=False, verbose=False):
    """
    Validate all information files in a directory.

    Args:
        info_dir (:class:`Path`): directory where files reside
        drilldown (bool): drill down through subdirectories
        n_sublevels (int): number of sublevels to print_directory
        configs (boolean): print configurations instead of object
    Returns:
        false if any file failed
    """
    suffixes = ['.yaml', '.json']
    if not info_dir.is_dir():
        raise ValueError(f'"{info_dir}" is not a directory!')
    if drilldown is True:
        print(f'Printing files in and below directory {info_dir}')
        files = info_dir.glob('**/*.*')
    else:
        print(f'Validating files in directory {info_dir}')
        files = info_dir.glob('*.*')
    print(f'Will skip files whose suffix is not in "{suffixes}"')
    for file in files:
        if file.suffix.lower() not in suffixes:
            print(f'Skipping {str(file.relative_to(info_dir))}')
            continue
        print_single_file(str(file), n_sublevels=n_sublevels,
                          configs=configs, verbose=verbose)


def main():
    """
    Entry point for obsinfo-print. Print an information file according to its
    type, and levels below up to the specified level.

     Captures all exceptions

    """

    args = retrieve_arguments()

    if args.input.is_file():
        print_single_file(str(args.input), args.n_levels, args.configs,
                          args.verbose)
    elif args.input.is_dir():
        print_directory(args.input, args.n_levels, args.configs, args.drilldown,
                        args.verbose)


def retrieve_arguments():
    """
    Retrieve arguments from command line.

    Setup several status variables and get information file name

    Returns:
        (dict): all status variables and information file name.
    """

    # Parse the arguments
    parser = ArgumentParser(prog="obsinfo-print")

    # flags
    parser.add_argument("-n", "--n_levels", type=int, default=1,
                        help="Prints up to N levels")
    parser.add_argument("-d", "--debug", action='store_true', default=False,
                        help="Print traceback for exceptions")
    parser.add_argument("--drilldown", action='store_true', default=False,
                        help="Drill down through all subdirectories (if a "
                        "directory was specified)")
    parser.add_argument("--configs", action='store_true', default=False,
                        help="Print configurations instead of object")
    parser.add_argument("--verbose", action='store_true', default=False,
                        help="Be verbose")
    # positional arguments
    parser.add_argument("input", type=str,
                        help="Information file or directory to be validated. "
                             "If a directory, tests all files in the "
                             "directory.  If 'DATAPATH', will test all files "
                             "in the DATAPATH (sets --drilldown and "
                             "--n_sublevels=0)")

    args = parser.parse_args()

    if args.input == 'DATAPATH':
        args.input = Datapath().datapath_list[0]
        args.drilldown = True
        args.n_levels = 0
        print('Validating first DATAPATH dir')
    args.input = Path(args.input)
    if not args.input.is_absolute():
        args.input = Path(os.getcwd()).joinpath(args.input).resolve()

    return args


def init_logging():
    """
    Create or open a rotating logging file and add it to ObsinfoConfiguration

    :returns: object of Logger class
    """
    logfile = Path.home().joinpath('.obsinfo', 'obsinfolog-validate')

    logger = logging.getLogger("obsinfo")

    logger.setLevel(logging.DEBUG)
    # add a rotating handler with 200K (approx) files and just two files
    handler = RotatingFileHandler(logfile, maxBytes=200000,
                                  backupCount=2)
    frmt = logging.Formatter(fmt='%(asctime)s %(levelname)-8s %(message)s',
                             datefmt='%Y-%m-%d %H:%M:%S')
    handler.setFormatter(frmt)
    logger.addHandler(handler)

    return logger


if __name__ == '__main__':
    main()
