# coding: utf-8

from __future__ import absolute_import
from typing import List, Dict
import os, sys
import hashlib
import traceback

from celery import Celery

from .base_model_ import Model
from ..util import deserialize_model
from .phenotype import Phenotype
from .worktable import (
    create_worktable_file,
    get_worktable_summary,
    get_worktable_genomedata,
    get_worktable_local_manhattan_data,
    get_worktable_local_heatmap_data,
)
from .plots import create_global_plot, create_quadrant_plot
from jass.config import config

app = Celery("tasks", broker="pyamqp://guest@localhost//")


class Project(Model):

    DOES_NOT_EXIST = "DOES_NOT_EXIST"

    CREATING = "CREATING"

    READY = "READY"

    ERROR = "ERROR"

    def __init__(self, id: str = None, phenotypes: List[Phenotype] = None):
        """
        Project - a project (list of phenotypes)

        :param id: project ID.
        :type id: str
        """
        self.swagger_types = {"id": str, "status": str, "phenotypes": List[Phenotype]}

        self.attribute_map = {
            "id": "id",
            "status": "status",
            "phenotypes": "phenotypes",
        }

        self._id = id
        self._phenotypes = phenotypes
        if self._id is None:
            self._id = self.get_id()

    @classmethod
    def from_dict(cls, dikt) -> "Project":
        """
        Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Project.
        :rtype: Project
        """
        return deserialize_model(dikt, cls)

    @property
    def id(self) -> str:
        """
        Gets the id of this Project.

        :return: The id of this Project.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id: str):
        """
        Lists the id of this Project.

        :param id: The id of this Project.
        :type id: str
        """

        self._id = id

    @property
    def phenotypes(self) -> List[Phenotype]:
        """
        Gets the phenotypes list for this project.

        :return: The phenotypes.
        :rtype: str
        """
        return self._phenotypes

    @phenotypes.setter
    def cohort(self, phenotypes: List[Phenotype]):
        """
        Lists the phenotypes list for this project.

        :param phenotypes: The phenotypes.
        :type phenotypes: str
        """

        self._phenotypes = phenotypes

    def get_folder_path(self):
        return os.path.join(config["DATA_DIR"], "project_{}".format(self.id))

    def get_worktable_path(self):
        return os.path.join(self.get_folder_path(), "workTable.hdf5")

    def get_csv_path(self):
        return os.path.join(self.get_folder_path(), "workTable.csv")

    def get_project_summary_statistics(self):
        return get_worktable_summary(self.get_worktable_path())

    def get_project_genomedata(self):
        return get_worktable_genomedata(self.get_worktable_path())

    def get_project_local_manhattan_data(self, chromosome: str, region: str):
        return get_worktable_local_manhattan_data(
            self.get_worktable_path(), chromosome, region
        )

    def get_project_local_heatmap_data(self, chromosome: str, region: str):
        return get_worktable_local_heatmap_data(
            self.get_worktable_path(), chromosome, region
        )

    def get_id(self):
        m = hashlib.md5()
        for phenotype_id in [phenotype.id for phenotype in self._phenotypes]:
            m.update(str(phenotype_id).encode("utf-8"))
        return m.hexdigest()

    def get_global_manhattan_plot_path(self):
        return os.path.join(self.get_folder_path(), "Manhattan_Plot_Omnibus.png")

    def get_quadrant_plot_path(self):
        return os.path.join(self.get_folder_path(), "Quadrant_Plot_Omnibus.png")

    @property
    def status(self):
        if not os.path.exists(self.get_folder_path()):
            return Project.DOES_NOT_EXIST
        else:
            worktable_status = get_file_status(self.get_worktable_path())
            global_manhattan_status = get_file_status(
                self.get_global_manhattan_plot_path()
            )
            quadrant_plot_status = get_file_status(self.get_quadrant_plot_path())
            return {
                # WARNING: project status is hacked so that everything is ready
                # only once the final step has completed.
                # This avoids the apparent "corrupted hdf5" file situation
                # "worktable": worktable_status,
                # "global_manhattan": global_manhattan_status,
                "worktable": quadrant_plot_status,
                "global_manhattan": quadrant_plot_status,
                "quadrant_plot_status": quadrant_plot_status,
            }


def get_file_building_tb_path(file_path):
    return file_path + ".log"


def get_file_status(file_path):
    if os.path.exists(file_path):
        return Project.READY
    elif os.path.exists(get_file_building_tb_path(file_path)):
        return Project.ERROR
    else:
        return Project.CREATING


@app.task
def create_project_global_plot(worktable_path, plot_path):
    try:
        create_global_plot(worktable_path, plot_path)
    except Exception as e:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        log_path = get_file_building_tb_path(plot_path)
        log_fh = open(log_path, "w")
        traceback.print_exception(exc_type, exc_value, exc_traceback, file=log_fh)
        log_fh.close()


@app.task
def create_project_quadrant_plot(worktable_path, plot_path):
    try:
        create_quadrant_plot(worktable_path, plot_path)
    except Exception as e:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        log_path = get_file_building_tb_path(plot_path)
        log_fh = open(log_path, "w")
        traceback.print_exception(exc_type, exc_value, exc_traceback, file=log_fh)
        log_fh.close()


@app.task
def create_project_data(
    phenotype_ids, init_table_path, worktable_path, global_plot_path, quadrant_plot_path, csv_file=None
):
    try:
        create_worktable_file(phenotype_ids, init_table_path, worktable_path, False, csv_file=csv_file)
    except Exception as e:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        log_path = get_file_building_tb_path(worktable_path)
        log_fh = open(log_path, "w")
        traceback.print_exception(exc_type, exc_value, exc_traceback, file=log_fh)
        log_fh.close()
        return
    create_project_global_plot.delay(worktable_path, global_plot_path)
    create_project_quadrant_plot.delay(worktable_path, quadrant_plot_path)


def create_project(phenotype_ids: List[str], available_phenotypes: List[Phenotype]):
    available_phenotype_ids = [phenotype.id for phenotype in available_phenotypes]
    unavailable_requested_ids = set(phenotype_ids).difference(
        set(available_phenotype_ids)
    )
    if len(unavailable_requested_ids) > 0:
        raise Exception()  # FIXME with a nice exception
    phenotypes = [
        phenotype for phenotype in available_phenotypes if phenotype.id in phenotype_ids
    ]
    project = Project(phenotypes=phenotypes)
    folder_path = project.get_folder_path()
    # if project does not exist
    if project.status == Project.DOES_NOT_EXIST:
        os.makedirs(folder_path)
        create_project_data.delay(
            phenotype_ids,
            os.path.join(config["DATA_DIR"], "initTable.hdf5"),
            project.get_worktable_path(),
            project.get_global_manhattan_plot_path(),
            project.get_quadrant_plot_path(),
            csv_file=project.get_csv_path()
        )
    return project
