# -*- coding: utf-8 -*-
import logging
from typing import Dict, List, Set
import re

import attr
from collections import defaultdict

from .parsers import parse_fasta


@attr.s
class SubtypeCounts:
    subtype = attr.ib()
    refpositions = attr.ib(default=None)
    subtype_kmer_count = attr.ib(default=0, validator=attr.validators.instance_of(int))
    positive_kmer_count = attr.ib(default=0, validator=attr.validators.instance_of(int))
    negative_kmer_count = attr.ib(default=0, validator=attr.validators.instance_of(int))
    all_kmer_count = attr.ib(default=0, validator=attr.validators.instance_of(int))

    @subtype.validator
    def _check_subtype(self, attribute, value):
        REGEX_SUBTYPE = re.compile(r'^(\d*[^\d\.]*(\.\d+)*)$')
        if value is None or value == '':
            raise ValueError('Subtype cannot be None or empty string')
        if not REGEX_SUBTYPE.match(value):
            raise ValueError(
                'Invalid subtype specified! The "{}" kmer is not formatted correctly. It must be numbers delimited by "." (periods)'.format(value))
        return value 

    @subtype_kmer_count.validator
    def _check_subtype_kmer_count(self, attribute, value):
        if value == 0:
            raise ValueError('Subtype kmer count cannot be zero!')

    @positive_kmer_count.validator
    def _check_positive_kmer_count(self, attribute, value):
        if value < self.subtype_kmer_count:
            raise ValueError(
                'Subtype {}: Number of all subtype positive kmers (n={}) cannot be less than the number of subtype specific kmers (n={})'.format(
                    self.subtype,
                    value,
                    self.subtype_kmer_count))
        if value > self.all_kmer_count:
            raise ValueError(
                'Subtype {}: Number of all subtype positive kmers (n={}) cannot exceed number of all matching kmers for subtype (n={})'.format(
                    self.subtype,
                    value,
                    self.all_kmer_count))
        if value == 0:
            raise ValueError('Subtype {}: Number of all subtype positive kmers cannot be zero!'.format(self.subtype))


def _kmers(kmers_fasta: str) -> (Dict[str, List[str]], Dict[str, List[str]], Set[int]):
    kmers = defaultdict(list)
    neg_kmers = defaultdict(list)
    sizes = set()
    for h, s in parse_fasta(kmers_fasta):
        sizes.add(len(s))
        _, st = h.split('-')
        if 'negative' not in h:
            kmers[st].append(h)
        else:
            neg_kmers[st].append(h)
    return kmers, neg_kmers, sizes


def subtype_counts(scheme_fasta: str) -> Dict[str, SubtypeCounts]:
    subtype_counts = {}
    kmers, neg_kmers, sizes = _kmers(scheme_fasta)
    if len(sizes) > 1:
        logging.warning('Not all markers in "%s" of the same size! %s', scheme_fasta, sizes)
    n_kmers_total = sum([len(vs) for vs in kmers.values()])
    ks = [x for x in kmers.keys()]
    ks.sort()
    for k in ks:
        st_pos_count_rest = 0
        subtypes_set = {k}
        if len(k) > 1:
            *r, _ = k.split('.')
            for i in range(len(r)):
                sub_k = '.'.join(r[:i + 1])
                subtypes_set.add(sub_k)
                pos_kmers = kmers[sub_k]
                st_pos_count_rest += len(pos_kmers)
        st_count = len(kmers[k])
        st_count_pos = st_pos_count_rest + st_count
        st_neg_count = sum([len(v) for k,v in neg_kmers.items() if k not in subtypes_set])

        subtype_count = SubtypeCounts(subtype=k,
                                      refpositions={int(v.split('-')[0]) for v in kmers[k]},
                                      subtype_kmer_count=st_count,
                                      positive_kmer_count=st_count_pos,
                                      negative_kmer_count=st_neg_count,
                                      all_kmer_count=st_neg_count + st_count_pos)
        subtype_counts[k] = subtype_count
    return subtype_counts
