import logging
import subprocess as sp

from collections import OrderedDict
from pathlib import Path

import bakta.config as cfg
import bakta.constants as bc
import bakta.so as so
import bakta.utils as bu


log = logging.getLogger('CRISPR')


def predict_crispr(genome: dict, contigs_path: Path):
    """Predict CRISPR arrays with PILER-CR."""

    output_path = cfg.tmp_path.joinpath('crispr.txt')
    cmd = [
        'pilercr',
        '-in', str(contigs_path),
        '-out', str(output_path),
        '-noinfo',  # omit help in output
        '-quiet'  # silent mode
    ]
    log.debug('cmd=%s', cmd)
    proc = sp.run(
        cmd,
        cwd=str(cfg.tmp_path),
        env=cfg.env,
        stdout=sp.PIPE,
        stderr=sp.PIPE,
        universal_newlines=True
    )
    if(proc.returncode != 0):
        log.debug('stdout=\'%s\', stderr=\'%s\'', proc.stdout, proc.stderr)
        log.warning('CRISPRs failed! pilercr-error-code=%d', proc.returncode)
        raise Exception(f'PILER-CR error! error code: {proc.returncode}')

    # parse orfs
    crispr_arrays = []
    contigs = {c['id']: c for c in genome['contigs']}
    with output_path.open() as fh:
        contig_id = None
        skip_lines = True
        for line in fh:
            line = line.strip()
            if(line == 'SUMMARY BY POSITION'):
                skip_lines = False
            elif(skip_lines is False):
                if(len(line) > 0):
                    if(line[0] == '>'):
                        contig_id = line[1:]
                    elif(line[0] != 'A' and line[0] != '='):
                        cols = line.split()
                        if(len(cols) == 8):
                            (array_id, contig, position, length, copies, repeat_length, spacer_length, repeat_consensus) = cols
                        else:
                            (array_id, contig, position, length, copies, repeat_length, spacer_length, distance, repeat_consensus) = cols

                        crispr = OrderedDict()
                        crispr['type'] = bc.FEATURE_CRISPR
                        crispr['contig'] = contig_id
                        crispr['start'] = int(position)
                        crispr['stop'] = int(position) + int(length) - 1
                        crispr['strand'] = bc.STRAND_UNKNOWN
                        crispr['product'] = f'CRISPR array with {copies} repeats of length {repeat_length}, consensus sequence {repeat_consensus} and spacer length {spacer_length}'
                        crispr['spacer_length'] = int(spacer_length)
                        crispr['repeat_length'] = int(repeat_length)
                        crispr['repeats'] = int(copies)
                        crispr['repeat_consensus'] = repeat_consensus
                        crispr['db_xrefs'] = [so.SO_CRISPR.id]

                        nt = bu.extract_feature_sequence(crispr, contigs[contig_id])  # extract nt sequences
                        crispr['nt'] = nt

                        crispr_arrays.append(crispr)
                        log.info(
                            'contig=%s, start=%i, stop=%i, spacer-length=%i, repeat-length=%i, # repeats=%i, repeat-consensus=%s, nt=[%s..%s]',
                            crispr['contig'], crispr['start'], crispr['stop'], crispr['spacer_length'], crispr['repeat_length'], crispr['repeats'], crispr['repeat_consensus'], nt[:10], nt[-10:]
                        )
    log.info('predicted=%i', len(crispr_arrays))
    return crispr_arrays
