# -*- coding: utf-8 -*-
import logging
from pathlib import Path
from typing import Iterator

import alchy
import sqlalchemy as sqa

from cgstats.db import api, models

LOG = logging.getLogger(__name__)


class StatsAPI(alchy.Manager):

    Project = models.Project
    Sample = models.Sample
    Unaligned = models.Unaligned
    Supportparams = models.Supportparams
    Datasource = models.Datasource
    Demux = models.Demux
    Flowcell = models.Flowcell

    def __init__(self, config: dict):
        alchy_config = dict(SQLALCHEMY_DATABASE_URI=config['cgstats']['database'])
        super(StatsAPI, self).__init__(config=alchy_config, Model=models.Model)
        self.root_dir = Path(config['cgstats']['root'])

    def flowcell(self, flowcell_name: str) -> dict:
        """Fetch information about a flowcell."""
        record = self.Flowcell.query.filter_by(flowcellname=flowcell_name).first()
        data = {
            'name': record.flowcellname,
            'sequencer': record.demux[0].datasource.machine,
            'sequencer_type': record.hiseqtype,
            'date': record.time,
            'samples': []
        }
        for sample_obj in self.flowcell_samples(record):
            raw_samplename = sample_obj.samplename.split('_', 1)[0]
            curated_samplename = raw_samplename.rstrip('AB')
            sample_data = {
                'name': curated_samplename,
                'reads': 0,
                'fastqs': [],
            }
            for fc_data in self.sample_reads(sample_obj):
                if fc_data.type == 'hiseqga' and fc_data.q30 >= 80:
                    sample_data['reads'] += fc_data.reads
                elif fc_data.type == 'hiseqx' and fc_data.q30 >= 75:
                    sample_data['reads'] += fc_data.reads
                elif fc_data.type == 'novaseq' and fc_data.q30 >= 75:
                    sample_data['reads'] += fc_data.reads
                else:
                    LOG.warning(f"q30 too low for {curated_samplename} on {fc_data.name}:"
                                f"{fc_data.q30} < {80 if fc_data.type == 'hiseqga' else 75}%")
                    continue
                for fastq_path in self.fastqs(fc_data.name, sample_obj):
                    if self.is_lane_pooled(flowcell_obj=record, lane=fc_data.lane):
                        if 'Undetermined' in str(fastq_path):
                            continue
                    sample_data['fastqs'].append(str(fastq_path))
            data['samples'].append(sample_data)

        return data

    def flowcell_samples(self, flowcell_obj: models.Flowcell) -> Iterator[models.Sample]:
        """Fetch all the samples from a flowcell."""
        return (
            self.Sample.query
            .join(models.Sample.unaligned, models.Unaligned.demux)
            .filter(models.Demux.flowcell == flowcell_obj)
        )

    def is_lane_pooled(self, flowcell_obj: models.Flowcell, lane: str) -> bool:
        """Check whether a lane is pooled or not."""
        query = (
            self.session.query(
               sqa.func.count(models.Unaligned.sample_id).label('sample_count')
            )
            .join(
               models.Unaligned.demux,
            )
            .filter(models.Demux.flowcell == flowcell_obj)
            .filter(models.Unaligned.lane == lane)
        )
        return query.first().sample_count > 1

    def sample_reads(self, sample_obj: models.Sample) -> Iterator:
        """Calculate reads for a sample."""
        query = (
            self.session.query(
                models.Flowcell.flowcellname.label('name'),
                models.Flowcell.hiseqtype.label('type'),
                models.Unaligned.lane,
                sqa.func.sum(models.Unaligned.readcounts).label('reads'),
                sqa.func.min(models.Unaligned.q30_bases_pct).label('q30'),
            )
            .join(
                models.Flowcell.demux,
                models.Demux.unaligned
            )
            .filter(models.Unaligned.sample == sample_obj)
            .group_by(models.Flowcell.flowcellname)
        )
        return query

    def sample(self, sample_name: str) -> models.Sample:
        """Fetch a sample for the database by name."""
        return api.get_sample(sample_name).first()

    def fastqs(self, flowcell: str, sample_obj: models.Sample) -> Iterator[Path]:
        """Fetch FASTQ files for a sample."""
        base_pattern = "*{}/Unaligned*/Project_*/Sample_{}/*.fastq.gz"
        alt_pattern = "*{}/Unaligned*/Project_*/Sample_{}_*/*.fastq.gz"
        for fastq_pattern in (base_pattern, alt_pattern):
            pattern = fastq_pattern.format(flowcell, sample_obj.samplename)
            files = self.root_dir.glob(pattern)
            yield from files
