""" Read isomiR GFF files"""

import os
from collections import defaultdict, Counter

import mirtop.libs.logger as mylog
from mirtop.mirna import mapper
from mirtop.mirna.realign import expand_cigar, make_id
from mirtop.gff.body import paste_columns, read_attributes
from mirtop.gff.body import variant_with_nt
from mirtop.gff.classgff import feature

logger = mylog.getLogger(__name__)


def header(fn):
    """
    Custom header for isomiR-SEA importer.

    Args:
        *fn (str)*: file name with isomiR-SEA GFF output

    Returns:
        *(str)*: isomiR-SEA header string.
    """
    h = ""
    return h


def read_file(fn, args):
    """
    Read isomiR-SEA file and convert to mirtop GFF format.

    Args:
        *fn(str)*: file name with isomiR-SEA output information.

        *database(str)*: database name.

        *args(namedtuple)*: arguments from command line.
            See *mirtop.libs.parse.add_subparser_gff()*.

    Returns:
        *reads (nested dicts)*:gff_list has the format as
            defined in *mirtop.gff.body.read()*.

    """
    database = args.database
    gtf = args.gtf
    sep = " " if args.out_format == "gtf" else "="
    map_mir = mapper.read_gtf_to_mirna(gtf)
    reads = defaultdict(dict)
    reads_in = 0
    sample = os.path.splitext(os.path.basename(fn))[0]
    hits = _get_hits(fn)
    logger.debug("ISOMIRSEA::SAMPLE::%s" % sample)
    with open(fn) as handle:
        for line in handle:
            cols = line.strip().split("\t")
            attr = read_attributes(line, "=")
            query_name = attr['TS']
            query_sequence = attr['TS'].replace("U", "T")
            start = int(cols[3])
            end = int(cols[4])
            isomirseq_iso = attr['ISO']
            if query_name not in reads and query_sequence == None:
                continue
            if query_sequence and query_sequence.find("N") > -1:
                continue
            counts = attr["TC"]
            chrom = cols[0]
            # logger.debug("SEQBUSTER:: cigar {cigar}".format(**locals()))
            cigar = attr['CI'].replace("U", "T")
            idu = make_id(query_sequence)
            isoformat = cigar2variants(cigar, query_sequence, attr['ISO'])
            logger.debug("\nISOMIRSEA::NEW::query: {query_sequence}\n"
                         "  precursor {chrom}\n"
                         "  name: {query_name}\n"
                         "  idu: {idu}\n"
                         "  start: {start}\n"
                         "  cigar: {cigar}\n"
                         "  iso: {isoformat}\n"
                         "  variant: {isoformat}".format(**locals()))
            source = "isomiR" if isoformat != "NA" else "ref_miRNA"
            strand = "+"
            database = cols[1]
            mirName = attr['MIN'].split()[0]
            preName = attr['PIN'].split()[0]
            score = "."
            Filter = attr['FILTER']
            isotag = attr['ISO']
            tchrom, tstart = _genomic2transcript(map_mir[mirName],
                                                 chrom, start)
            start = start if not tstart else tstart
            chrom = chrom if not tstart else tchrom
            end = start + len(query_sequence)
            hit = hits[idu]
            fields = {'seq_name': query_sequence, 'idseq': idu,
                      'name': mirName, 'parent': preName,
                      'variant': isoformat, 'cigar': cigar,
                      'counts': counts, 'filter': Filter,
                      'hits': hit, 'chrom': chrom,
                      'start': start, 'end': end,
                      'database': database, 'source': source,
                      'score': score, 'strand': strand}
            # TODO: convert to genomic if args.out_genomic
            line = feature(fields).line
            if args.add_extra:
                extra = variant_with_nt(line, args.precursors, args.matures)
                line = "%s Changes %s;" % (line, extra)

            line = paste_columns(feature(line), sep=sep)
            if start not in reads[chrom]:
                reads[chrom][start] = []
            if Filter == "Pass":
                reads_in += 1
                reads[chrom][start].append([idu, chrom, counts, sample, line])

    logger.info("Hits: %s" % reads_in)
    return reads


def _get_hits(fn):
    hits = Counter()
    with open(fn) as handle:
        for line in handle:
            attr = read_attributes(line, "=")
            query_sequence = attr['TS'].replace("U", "T")
            if query_sequence and query_sequence.find("N") > -1:
                continue
            idu = make_id(query_sequence)
            hits[idu] += 1
    return hits


def cigar2variants(cigar, sequence, tag):
    """From cigar to Variants in GFF format"""
    pos = 0
    iso5p = 0
    logger.debug("\nISOMIRSEA:: expanded: %s" % expand_cigar(cigar))
    for l in expand_cigar(cigar):
        if l == "I":
            iso5p -= 1
        elif l == "D":
            iso5p += 1
        else:
            break
    iso3p = 0
    for l in reversed(expand_cigar(cigar)):
        if l == "I":
            iso3p += 1
        elif l == "D":
            iso3p -= 1
        else:
            break
    isosnp = []
    for l in expand_cigar(cigar):
        if l in ['A', 'T', 'C', 'G' ]:
            isosnp.append([pos, sequence[pos], l])
        if l in ['D']:
            continue
        pos += 1
    iso5p = "iso_5p:%s" % _fix(iso5p) if iso5p else ""
    if tag[-1] == "T" or iso3p < 0:
        iso3p = "iso_3p:%s" % _fix(iso3p) if iso3p else ""
    else:
        iso3p = "iso_add3p:%s" % iso3p if iso3p else ""

    variant = ""
    for iso in [iso5p, iso3p, _define_snp(isosnp)]:
        if iso:
            variant += "%s," % iso

    variant = "NA;" if not variant else variant
    return variant[:-1]


def _define_snp(subs):
    value = ""
    logger.debug("\nISOMIRSEA:: subs %s" % subs)
    for sub in subs:
        if sub:
            if sub[0] > 1 and sub[0] < 8:
                value += "iso_snv_seed,"
            elif sub[0] == 8:
                value += "iso_snv_central_offset,"
            elif sub[0] > 8 and sub[0] < 13:
                value += "iso_snv_central,"
            elif sub[0] > 12 and sub[0] < 18:
                value += "iso_snv_central_supp,"
            else:
                value += "iso_snv,"
    return value[:-1]


def _fix(n):
    if n > 0:
        return "+%s" % n
    return n


def _genomic2transcript(code, chrom, pos):
    for ref in code:
        if _is_chrom(chrom, code[ref][0]):
            if _is_inside(pos, code[ref][1:3]):
                return [ref, _transcript(pos, code[ref][1:4])]
    return [None, None]


def _is_chrom(chrom, annotated):
    logger.debug("TRANSCRIPT::CHROM::read position %s and db position %s" % (chrom, annotated))
    if chrom == annotated:
        return True
    if chrom == annotated.replace("chr", ""):
        return True
    return False


def _is_inside(pos, annotated):
    logger.debug("TRANSCRIPT::INSIDE::read position %s and db position %s" % (pos, annotated))
    if pos > annotated[0] and pos < annotated[1]:
        return True
    return False


def _transcript(pos, annotated):
    logger.debug("TRANSCRIPT::TRANSCRIPT::read position %s and db position %s" % (pos, annotated))
    if annotated[2] == "+":
        return pos - annotated[0]
    elif annotated[2] == "-":
        return annotated[1] - pos
    raise ValueError("Strand information is incorrect %s" % annotated[3])
