#!/usr/bin/env python

from .helper import check_file, return_filehandle
import os
import sys
import logging
import re
import subprocess
import hashlib
import requests
import json
from glob import glob
from os import path

sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))


class genome_main:
    def __init__(self, detector, **kwargs):
        self.detector = detector
        self.target = detector.target
        self.logger = detector.logger

    def run(self):
        """Runs checks"""
        logger = self.logger
        target = self.target
        logger.info("Performing Naming Checks for {}\n".format(target))
        if not self.check_genome_main():
            logger.error("Naming Check FAILED")
            return False
        logger.info("Naming Looks Correct\n")
        logger.info("Checking Genome FASTA\n")
        if not self.check_genome_fasta():
            logger.error("FASTA Check FAILED")
            return False
        logger.info("ALL GENOME CHECKS PASSED\n")
        return True

    def check_genome_main(self):
        """accepts a list of genome attributes split by "."

           https://github.com/LegumeFederation/datastore/issues/23

           checks these file attributes to ensure they are correct
        """
        target = self.target
        logger = self.logger
        attr = os.path.basename(target).split(".")  # split on delimiter
        if len(attr) != 7:  # should be 7 fields
            logger.error("File did not have 7 fields! {}".format(attr))
            return False
        if len(attr[0]) != 5:  # should be 5 letter prefix
            logger.error("File must have 5 letter prefix, not {}".format(attr[0]))
            return False
        if not attr[2].startswith("gnm"):  # should be gnm type
            logger.error("File should have gnm in field 3, not {}".format(attr[2]))
            return False
        gnm_v = attr[2].replace("gnm", "")
        try:
            int(gnm_v)
        except ValueError:  # best way to check for int in python2
            logger.error("gnm version must be integer not {}".format(gnm_v))
            return False
        if len(gnm_v.split(".")) > 1:  # check for float
            logger.error("gnm version must be integer not {}".format(gnm_v))
            return False
        if not attr[5] == "fna":  # should be fna type
            logger.error("File should be fna not {}".format(attr[5]))
            return False
        if not attr[6] == "gz":  # should be gzip compressed
            logger.error("Last field should be gz, not {}".format(attr[6]))
            return False
        logger.info("Genome Naming Looks Correct")
        return True

    def check_genome_fasta(self):
        """Confirms that headers in fasta genome_main conform with standard

           PUT SOME RULE REFERENCE HERE
        """
        logger = self.logger
        fasta = self.target  # get fasta file
        attr = os.path.basename(fasta).split(".")  # get attributes for naming
        true_header = ".".join(attr[:3])
        fh = return_filehandle(fasta)  # get file handle, text/gz
        # grab header and description
        re_header = re.compile(r"^>(\S+)\s*(.*)")
        passed = True
        with fh as gopen:
            for line in gopen:
                line = line.rstrip()
                if not line:
                    continue
                if re_header.match(line):  # check for fasta header
                    hid = re_header.search(line)
                    if hid:
                        logger.debug(hid.groups(0))
                        if isinstance(hid, str):  # check for tuple
                            hid = hid.groups(0)
                        else:
                            hid = hid.groups(0)[0]  # get id portion of header
                    else:
                        logger.error("Header {} looks odd...".format(line))
                        return False
                    logger.debug(hid)
                    self.detector.fasta_ids[hid] = 1
                    standard_header = true_header + "." + hid
                    if not hid.startswith(true_header):
                        logger.warning(
                            (
                                "Inconsistency {} ".format(hid)
                                + "Should be {}".format(standard_header)
                            )
                        )
                        passed = False
        return passed


class gene_models_main:
    def __init__(self, detector, **kwargs):
        self.detector = detector
        self.target = detector.target
        self.logger = detector.logger
        self.fasta_ids = detector.fasta_ids

    def run(self):
        """Run checks"""
        logger = self.logger
        target = self.target
        logger.info("Checking Gene Models File Naming...")
        if not self.check_gene_models_main():
            logger.error("Gene Model File Naming FAILED")
            return False
        logger.info("Naming Looks Correct\n")
        logger.info("Validating GFF3 with gt...")
        if not self.gt_gff3_validate():
            logger.error("gt GFF3 Validation FAILED")
            return False
        logger.info("GFF3 is Valid\n")
        logger.info("Checking Congruency Between Genome and Gene Models...")
        if not self.check_seqid_attributes():
            logger.error("Genome and Gene Models are not Congruent FAILED")
            return False
        logger.info("ALL GENE MODELS CHECKS PASSED\n")
        return True

    def check_gene_models_main(self):
        """accepts a list of annotation attributes split by "."

           https://github.com/LegumeFederation/datastore/issues/23

           checks these file attributes to ensure they are correct
        """
        target = self.target
        logger = self.logger
        attr = os.path.basename(target).split(".")  # split on delimiter
        if len(attr) != 8:  # should be 8 fields
            logger.error("File did not have 7 fields! {}".format(attr))
            return False
        if len(attr[0]) != 5:  # should be 5 letter prefix
            logger.error("File must have 5 letter prefix, not {}".format(attr[0]))
            return False
        if not attr[2].startswith("gnm"):  # should be gnm type
            logger.error("File should have gnm in field 3, not {}".format(attr[2]))
            return False
        gnm_v = attr[2].replace("gnm", "")
        try:
            int(gnm_v)
        except ValueError:  # best way to check for int in python2
            logger.error("gnm version must be integer not {}".format(gnm_v))
            return False
        if len(gnm_v.split(".")) > 1:  # check for float
            logger.error("gnm version must be integer not {}".format(gnm_v))
            return False
        if not attr[3].startswith("ann"):  # should be gnm type
            logger.error("File should have ann in field 4, not {}".format(attr[2]))
            return False
        ann_v = attr[3].replace("ann", "")
        try:
            int(ann_v)
        except ValueError:  # best way to check for int in python2
            logger.error("ann version must be integer not {}".format(ann_v))
            return False
        if len(ann_v.split(".")) > 1:  # check for float
            logger.error("ann version must be integer not {}".format(gnm_v))
            return False
        if not attr[6] == "gff3":  # should be gff3 type
            logger.error("File should be gff3 not {}".format(attr[6]))
            return False
        if not attr[7] == "gz":  # should be gzip compressed
            logger.error("Last field should be gz, not {}".format(attr[6]))
            return False
        return True

    def gt_gff3_validate(self):
        """Confirms that gff3 files pass gt validation

           https://github.com/LegumeFederation/datastore/issues/23
        """
        gff = self.target
        logger = self.logger
        gff_name = os.path.basename(gff)
        gt_report = "./{}_gt_gff3validator_report.txt".format(gff_name)
        gt_cmd = "(gt gff3validator {} 2>&1) > {}".format(gff, gt_report)
        logger.debug(gt_cmd)
        exit_val = subprocess.call(gt_cmd, shell=True)  # get gt exit_val
        logger.debug(exit_val)
        if exit_val:
            return False
        return True

    def check_seqid_attributes(self):
        """Confirms that gff3 seqid exists in genome_main if provided

           checks ID and Name from gff3 attributes field

           https://github.com/LegumeFederation/datastore/issues/23
        """
        gff = self.target
        logger = self.logger
        fasta_ids = self.fasta_ids  # list of FASTA IDS from Reference
        logger.debug(fasta_ids)
        fh = return_filehandle(gff)
        file_name = os.path.basename(gff)
        # ID should start with this string
        true_id = ".".join(file_name.split(".")[:4])
        true_name = file_name.split(".")[0]  # maybe this should include infra
        #        get_id_name = re.compile("^ID=(.+?);.*Name=(.+?);")
        get_id = re.compile("ID=([^;]+)")
        lines = 0
        seen = {}
        passed = True
        with fh as gopen:
            for line in gopen:
                line = line.rstrip()
                lines += 1
                if not line or line.startswith("#"):
                    continue
                columns = line.split("\t")  # get gff3 fields
                seqid = columns[0]  # seqid according to the spec
                seqid = seqid.rstrip()
                logger.debug(line)
                logger.debug(seqid)
                if self.fasta_ids:  # if genome_main make sure seqids exist
                    if seqid not in fasta_ids:  # fasta header check
                        logger.debug(seqid)
                        if seqid not in seen:
                            logger.error("{} not found in genome_main".format(seqid))
                            seen[seqid] = 1
                        passed = False
                feature_type = columns[2]  # get type
                attributes = columns[8]  # attributes ';' delimited
                logger.debug(feature_type)
                if feature_type != "gene":  # only check genes (for now)
                    continue
                #                if not get_id_name.match(attributes):  # check for ID and Name
                if not get_id.search(attributes):  # check for ID and Name
                    logger.error("No ID and Name attributes. line {}".format(lines))
                    passed = False
                else:
                    #                    groups = get_id_name.search(attributes).groups()
                    feature_id = get_id.search(attributes).group(1)
                    logger.debug(feature_id)
                    #                    if len(groups) != 2:  # should just be ID and Name
                    #                        logger.error('too many groups detected: {}'.format(
                    #                                                                      groups))
                    #                        passed = False
                    #                    (feature_id, feature_name) = groups
                    if not feature_id.startswith(true_id):  # check id
                        logger.error(
                            "gene feature id, should start with "
                            + "{} line {}".format(true_id, lines)
                        )
                        passed = False
        #                    if not feature_name.startswith(true_name):
        #                        logger.error('feature name, should start with ' +
        #                                     '{} line {}'.format(true_name, lines))
        #                        passed = False
        return passed


class protein:
    def __init__(self, detector, **kwargs):
        self.detector = detector
        self.target = detector.target
        self.logger = detector.logger

    def run(self):
        """Run Checks for protein"""
        logger = self.logger
        logger.info("protein check {}".format(self.target))
        if not self.check_protein():
            logger.error("protein file naming checks FAILED")
            return False
        logger.info("ALL protein CHCEKS PASSED")
        return True

    def check_protein(self):
        """accepts a list of annotation attributes split by "."

           https://github.com/LegumeFederation/datastore/issues/23

           checks these file attributes to ensure they are correct
        """
        target = self.target
        logger = self.logger
        attr = os.path.basename(target).split(".")  # split on delimiter
        if len(attr) != 8:  # should be 8 fields
            logger.error("File did not have 7 fields! {}".format(attr))
            return False
        if len(attr[0]) != 5:  # should be 5 letter prefix
            logger.error("File must have 5 letter prefix, not {}".format(attr[0]))
            return False
        if not attr[2].startswith("gnm"):  # should be gnm type
            logger.error("File should have gnm in field 3, not {}".format(attr[2]))
            return False
        gnm_v = attr[2].replace("gnm", "")
        try:
            int(gnm_v)
        except ValueError:  # best way to check for int in python2
            logger.error("gnm version must be integer not {}".format(gnm_v))
            return False
        if len(gnm_v.split(".")) > 1:  # check for float
            logger.error("gnm version must be integer not {}".format(gnm_v))
            return False
        if not attr[3].startswith("ann"):  # should be gnm type
            logger.error("File should have ann in field 4, not {}".format(attr[2]))
            return False
        ann_v = attr[3].replace("ann", "")
        try:
            int(ann_v)
        except ValueError:  # best way to check for int in python2
            logger.error("ann version must be integer not {}".format(ann_v))
            return False
        if len(ann_v.split(".")) > 1:  # check for float
            logger.error("ann version must be integer not {}".format(gnm_v))
            return False
        if not attr[6] == "faa":  # should be gff3 type
            logger.error("File should be gff3 not {}".format(attr[6]))
            return False
        if not attr[7] == "gz":  # should be gzip compressed
            logger.error("Last field should be gz, not {}".format(attr[6]))
            return False
        return True


class protein_primaryTranscript:
    def __init__(self, detector, **kwargs):
        self.detector = detector
        self.target = detector.target
        self.logger = detector.logger

    def run(self):
        """Run Checks for protein_primaryTranscript"""
        logger = self.logger
        logger.info("protein_primaryTranscript check {}".format(self.target))
        if not self.check_protein_primaryTranscript():
            logger.error("protein_primaryTranscript naming checks FAILED")
            return False
        logger.info("ALL protein_primaryTranscript CHCEKS PASSED")
        return True

    def check_protein_primaryTranscript(self):
        """accepts a list of annotation attributes split by "."

           https://github.com/LegumeFederation/datastore/issues/23

           checks these file attributes to ensure they are correct
        """
        target = self.target
        logger = self.logger
        attr = os.path.basename(target).split(".")  # split on delimiter
        if len(attr) != 8:  # should be 8 fields
            logger.error("File did not have 7 fields! {}".format(attr))
            return False
        if len(attr[0]) != 5:  # should be 5 letter prefix
            logger.error("File must have 5 letter prefix, not {}".format(attr[0]))
            return False
        if not attr[2].startswith("gnm"):  # should be gnm type
            logger.error("File should have gnm in field 3, not {}".format(attr[2]))
            return False
        gnm_v = attr[2].replace("gnm", "")
        try:
            int(gnm_v)
        except ValueError:  # best way to check for int in python2
            logger.error("gnm version must be integer not {}".format(gnm_v))
            return False
        if len(gnm_v.split(".")) > 1:  # check for float
            logger.error("gnm version must be integer not {}".format(gnm_v))
            return False
        if not attr[3].startswith("ann"):  # should be gnm type
            logger.error("File should have ann in field 4, not {}".format(attr[2]))
            return False
        ann_v = attr[3].replace("ann", "")
        try:
            int(ann_v)
        except ValueError:  # best way to check for int in python2
            logger.error("ann version must be integer not {}".format(ann_v))
            return False
        if len(ann_v.split(".")) > 1:  # check for float
            logger.error("ann version must be integer not {}".format(gnm_v))
            return False
        if not attr[6] == "faa":  # should be gff3 type
            logger.error("File should be gff3 not {}".format(attr[6]))
            return False
        if not attr[7] == "gz":  # should be gzip compressed
            logger.error("Last field should be gz, not {}".format(attr[6]))
            return False
        return True


class readme_md:  # need to populate this correctly later
    def __init__(self, detector, **kwargs):
        self.detector = detector
        self.target = detector.target
        self.logger = detector.logger
        self.fasta_ids = detector.fasta_ids

    def validate_checksum(self, md5_file, check_me):
        """Get md5 checksum for file and compare to expected"""
        logger = self.logger
        fh = return_filehandle(md5_file)
        hash_md5 = hashlib.md5()
        check_sum_target = ""
        switch = 0
        with fh as copen:
            for line in copen:
                line = line.rstrip()
                if not line or line.startswith("#"):
                    continue
                fields = line.split()
                check_sum = fields[0]
                filename = fields[1]
                logger.debug("check_sum: {}, filename: {}".format(check_sum, filename))
                if not check_sum and filename:
                    logger.error("Could not find sum and name for {}".format(line))
                if filename == os.path.basename(check_me):
                    logger.info("Checksum found for {}".format(filename))
                    check_sum_target = check_sum
                    switch = 1
        if not switch:
            logger.error("Could not find checksum for {}".format(check_me))
            sys.exit(1)
        with open(check_me, "rb") as copen:
            for chunk in iter(lambda: copen.read(4096), b""):  # 4096 buffer
                hash_md5.update(chunk)
        target_sum = hash_md5.hexdigest()  # get sum
        logger.debug(target_sum)
        logger.debug(check_sum_target)
        if target_sum != check_sum_target:  # compare sums
            logger.error(
                (
                    "Checksum for file {} {} ".format(check_me, target_sum)
                    + "did not match {}".format(check_sum_target)
                )
            )
            sys.exit(1)
        logger.info("Checksums checked out, moving on...")

    def validate_doi(self, readme):
        """Parse README.<key>.yml and get publication or dataset DOIs

           Uses http://www.doi.org/factsheets/DOIProxy.html#rest-api
        """
        logger = self.logger
        publication_doi = ""
        dataset_doi = ""
        object_dois = {}
