#!/usr/bin/env python3
# encoding: utf-8
#
# ** header v3.0
# This file is a part of the CaosDB Project.
#
# Copyright (C) 2021 Indiscale GmbH <info@indiscale.com>
# Copyright (C) 2021 Henrik tom Wörden <h.tomwoerden@indiscale.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# ** end header
#

"""An example script that illustrates how scripts can be used in conjunction
with the generic_analysis module.

The data model needed for this script is:

Analysis:
    sources: REFEERENCE
    scripts: FILE
    results: REFEERENCE
    mean_value: DOUBLE

Person:
    Email: TEXT

"""

import argparse
import logging
import sys
from argparse import RawTextHelpFormatter
from datetime import datetime

import caosdb as db
import matplotlib.pyplot as plt
import numpy as np
from caosadvancedtools.cfood import assure_property_is
from caosadvancedtools.crawler import apply_list_of_updates
from caosadvancedtools.guard import INSERT, UPDATE
from caosadvancedtools.guard import global_guard as guard
from caosadvancedtools.serverside.helper import send_mail as main_send_mail

# logging should be done like this in order to allow the caller script to
# direct the output.
logger = logging.getLogger(__name__)

# allow updates of existing entities
guard.set_level(level=UPDATE)


def send_mail(changes: [db.Entity], receipient: str):
    """ calls sendmail in order to send a mail to the curator about pending
    changes

    Parameters:
    -----------
    changes: The CaosDB entities in the version after the update.
    receipient: The person who shall receive the mail.
    """

    caosdb_config = db.configuration.get_config()
    text = """Dear Curator,
The following changes where done automatically.

{changes}
    """.format(changes="\n".join(changes))
    try:
        fro = caosdb_config["advancedtools"]["automated_updates.from_mail"]
    except KeyError:
        logger.error("Server Configuration is missing a setting for "
                     "sending mails. The administrator should check "
                     "'from_mail'.")
        return

    main_send_mail(
        from_addr=fro,
        to=receipient,
        subject="Automated Update",
        body=text)


def main(args):

    # auth_token is provided by the server side scripting API
    # use this token for authentication when creating a new connection
    if hasattr(args, "auth_token") and args.auth_token:
        db.configure_connection(auth_token=args.auth_token)
        logger.debug("Established connection")

    try:
        dataAnalysisRecord = db.Record(id=args.entityid).retrieve()
    except db.TransactionError:
        logger.error("Cannot retrieve Record with id ={}".format(
            args.entityid
        ))

    # The script may require certain information to exist. Here, we expect that
    # a sources Property exists that references a numpy file.
    # Similarly an InputDataSet could be used.

    if (dataAnalysisRecord.get_property("sources") is None
            or not db.apiutils.is_reference(
                dataAnalysisRecord.get_property("sources"))):

        raise RuntimeError("sources Refenrence must exist.")

    logger.debug("Found required data.")

    # ####### this core might be replaced by a call to another script ####### #
    # Download the data
    source_val = dataAnalysisRecord.get_property("sources").value
    npobj = db.File(
        id=(source_val[0]
            if isinstance(source_val, list)
            else source_val)).retrieve()
    npfile = npobj.download()
    logger.debug("Downloaded data.")
    data = np.load(npfile)

    # Plot data
    filename = "hist.png"
    plt.hist(data)
    plt.savefig(filename)

    mean = data.mean()
    # ####################################################################### #

    # Insert the result plot
    fig = db.File(file=filename,
                  path="/Analysis/results/"+str(datetime.now())+"/"+filename)
    fig.insert()

    # Add the mean value to the analysis Record
    # If such a property existed before, it is changed if necessary. The old
    # value will persist in the versioning of LinkAhead
    to_be_updated = db.Container()
    assure_property_is(
        dataAnalysisRecord,
        "mean_value",
        mean,
        to_be_updated=to_be_updated
    )

    # Add the file with the plot to the analysis Record
    # If a file was already referenced, the new one will be referenced instead.
    # The old file is being kept and is still referenced in an old version of
    # the analysis Record.
    assure_property_is(
        dataAnalysisRecord,
        "results",
        [fig.id],
        to_be_updated=to_be_updated
    )

    if len(to_be_updated) > 0:
        print(to_be_updated)
        apply_list_of_updates(to_be_updated, update_flags={})
        logger.debug("Update sucessful.")
        logger.info("The following Entities were changed:\n{}.".format(
            [el.id for el in to_be_updated])
        )

        # Send mails to people that are referenced.
        people = db.execute_query("FIND RECORD Person WHICH IS REFERENCED BY "
                                  "{}".format(dataAnalysisRecord.id))
        for person in people:
            if person.get_property("Email") is not None:
                send_mail([str(el) for el in to_be_updated],
                          receipient=person.get_property("Email").value)
        logger.debug("Mails send.")


def parse_args():
    parser = argparse.ArgumentParser(description=__doc__,
                                     formatter_class=RawTextHelpFormatter)
    parser.add_argument("--auth-token",
                        help="Token provided by the server for authentication")
    parser.add_argument("entityid",
                        help="The ID of the DataAnalysis Record.", type=int)

    return parser.parse_args()


if __name__ == "__main__":
    args = parse_args()
    sys.exit(main(args))
