"""
Author: Michel Peltriaux
Organization: Struktur- und Genehmigungsdirektion Nord, Rhineland-Palatinate, Germany
Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 26.10.22

"""
import zipfile
from datetime import datetime
from io import BytesIO

from django.core.mail import EmailMessage
from django.utils import timezone

from analysis.utils.excel.excel import TempExcelFile
from analysis.utils.report import TimespanReport
from codelist.models import KonovaCode
from codelist.settings import CODELIST_CONSERVATION_OFFICE_ID
from konova.management.commands.setup import BaseKonovaCommand
from konova.sub_settings.django_settings import DEFAULT_FROM_EMAIL, ADMINS


class Command(BaseKonovaCommand):
    help = "Generates reports for conservation offices"

    __from_key = "from"
    __to_key = "to"
    __for_key = "for"

    from_date = None
    to_date = None
    offices = None

    def add_arguments(self, parser):
        try:
            parser.add_argument(f"--{self.__from_key}", type=str, nargs="+")
            parser.add_argument(f"--{self.__to_key}", type=str, nargs="+")
            parser.add_argument(f"--{self.__for_key}", type=str, nargs="*")
        except ValueError as e:
            self._write_error(f"Argument error: {e}")
            exit(-1)

    def handle(self, *args, **options):
        self.__check_arguments(options)
        in_memory_zipped_reports = self.generate_reports_in_memory_zipped()
        self.send_mail_to_admins(in_memory_zipped_reports)

    def generate_reports_in_memory_zipped(self):
        """
        Generates in-memory reports and zips into in-memory zip file.

        Returns:

        """
        memory_zip = BytesIO()
        with zipfile.ZipFile(memory_zip, "w", compression=zipfile.ZIP_DEFLATED) as zf:
            for office in self.offices:
                self._write_warning(f"  Process report for {office.long_name}...")
                report = TimespanReport(office.id, self.from_date, self.to_date)
                excel_file = TempExcelFile(report.excel_template_path, report.excel_map)
                zf.writestr(zinfo_or_arcname=f"{office.long_name}_{self.from_date.date()}_{self.to_date.date()}.xlsx", data=excel_file.stream)
        self._write_success(f"Reports generated for {self.offices.count()} offices and zipped.")
        return memory_zip.getvalue()

    def __check_arguments(self, options):
        """
        Checks given parameters for validity

        Args:
            options ():

        Returns:

        """
        _from_value = options.get(self.__from_key, [None])[0]
        _to_value = options.get(self.__to_key, [None])[0]
        _for_value = options.get(self.__for_key, [])

        # Check ISO dates
        try:
            _from_date = timezone.make_aware(datetime.fromisoformat(_from_value))
            _to_date = timezone.make_aware(datetime.fromisoformat(_to_value))
        except Exception as e:
            self._write_warning(f"One of the dates is not in ISO format (YYYY-MM-DD). {e}")
            exit(-1)

        # Check conservation office IDs
        _filter = {
            "is_archived": False,
            "is_leaf": True,
            "code_lists__in": [CODELIST_CONSERVATION_OFFICE_ID],
        }
        offices = KonovaCode.objects.filter(**_filter)
        if _for_value is not None and len(_for_value) != 0:
            # Specifc offices requested
            offices = offices.filter(short_name__in=_for_value)
            all_requested_offices_exist = offices.count() == len(_for_value)
            if not all_requested_offices_exist:
                offices_short_name = set(offices.values_list("short_name", flat=True))
                missing_ids = []
                for val in _for_value:
                    if val not in offices_short_name:
                        missing_ids.append(val)
                self._write_warning(
                    f"Unknown offices: {missing_ids}"
                )
                exit(-1)

        self.offices = offices
        self.from_date = _from_date
        self.to_date = _to_date

    def send_mail_to_admins(self, attachment):
        office_names = [office.long_name for office in self.offices]
        admin_mails = [admin[1] for admin in ADMINS]

        date_from_date = self.from_date.date()
        date_to_date = self.to_date.date()

        mail = EmailMessage(
            subject="Konova reports generated",
            body=f"Zipped reports attached from {date_from_date} to {date_to_date} for {office_names}.",
            from_email=DEFAULT_FROM_EMAIL,
            to=admin_mails,
            attachments=[
                (
                    f"reports_{date_from_date}_{date_to_date}.zip",
                    attachment,
                    "application/zip"
                )
            ]
        )
        success = mail.send()
        if success == 1:
            self._write_success(f"Mails with zip as attachment sent to {admin_mails}.")
        else:
            self._write_error(f"Something went wrong during mail sending. Returned sending code was '{success}'")
