"""Log config for main application and its modules.

This module is maintained in another project. Future
improvements might include the generation of actual json
files, since as it is now it only generated json objects
and append them to the log file (instead of appending
them to an array and writing the array back as it would
be natural in a json file). Better control of naming would
also be great.

Additionally, large/verbose sentences, though useful are
difficult to present in various different medias. It would
be great if we could split a very large log into smaller
parts and log them all (at least in theory, this should
be tested in practice).
"""

from pathlib import Path

from os import environ

from os.path import join
from datetime import datetime
from json import dumps
from traceback import format_exception
from logging import Formatter, getLogger, DEBUG
from logging.handlers import RotatingFileHandler
from logging.handlers import TimedRotatingFileHandler


if "APPDATA" in environ:
    general_log_dir = Path(environ["APPDATA"])

else:
    general_log_dir = Path(environ["HOME"]) / ".local"

APP_LOGS_DIR = general_log_dir / "danittr" / "logs"

if not APP_LOGS_DIR.exists():
    APP_LOGS_DIR.mkdir(parents=True)

### Log level setup
# Change this value between the default levels
# depending on need. Use INFO level on normal operation.
LOG_LEVEL = DEBUG

APP_LOGGER = getLogger("app")
APP_LOGGER.setLevel(LOG_LEVEL)


### Custom formatter and tools


def json_serialize_traceback(exc_info):
    """Return json serializable version of exc_info"""
    exc_type, exc_value, exc_traceback = exc_info
    string_list = format_exception(exc_type, exc_value, exc_traceback)
    concatenated_string = "".join(string_list)
    return concatenated_string


class JsonLogFormatter(Formatter):
    """Outputs json logs."""

    def format(self, record):
        """Return a log record as json formated data.

        record
            A log record object automatically created."""
        if record.exc_info != None:
            exc_info = json_serialize_traceback(record.exc_info)
        else:
            exc_info = record.exc_info

        message = dict(
            name=record.name,
            timestamp=str(datetime.utcnow()),
            level=record.levelname,
            message=record.msg,
            exc_info=exc_info,
            lineno=record.lineno,
        )
        return dumps(message, indent=2, ensure_ascii=False)


json_formatter = JsonLogFormatter()


### Handlers


def custom_namer(name):
    """Custom namer function for handlers.

    name
        string automatically generated by the handlers.
    """
    return name + ".log"


## Handler to generate a record for each run

latest_filepath = str(APP_LOGS_DIR / "latest_run.log")
latest_run_handler = RotatingFileHandler(
    filename=latest_filepath, mode="a", maxBytes=0, backupCount=10, encoding="utf-8"
)

## Handler to generate a record for each day,
## regardless of quantity of runs
dailylog_path = str(APP_LOGS_DIR / "daily_runs.log")
daily_run_handler = TimedRotatingFileHandler(
    filename=dailylog_path,
    when="midnight",
    interval=1,
    backupCount=16,
    encoding="utf-8",
    utc=True,
)

### Final setup

latest_run_handler.setLevel(LOG_LEVEL)
latest_run_handler.setFormatter(json_formatter)
latest_run_handler.namer = custom_namer

daily_run_handler.setLevel(LOG_LEVEL)
daily_run_handler.setFormatter(json_formatter)
daily_run_handler.namer = custom_namer


APP_LOGGER.addHandler(latest_run_handler)
APP_LOGGER.addHandler(daily_run_handler)

APP_LOGGER.handlers[0].doRollover()

logger = APP_LOGGER.getChild(__name__)
logger.info("Configured logging.")
