#!/usr/bin/env python3
"""
Chapter 4: Python for Next Gen Biologist
Script: ch04_fastq_stream_parser.py
Description: Streaming FASTQ parser, ASCII Phred+33 score conversion, average read quality calculation,
             and quality filtering (Q30 threshold).
Usage:
    python ch04_fastq_stream_parser.py --input reads.fastq --min-q 30 --min-len 50
"""

import argparse
import sys
import gzip

def parse_fastq(filepath):
    """Generator yielding (header, seq, comment, qual_str) tuples from plain or gzipped FASTQ."""
    opener = gzip.open if filepath.endswith('.gz') else open
    with opener(filepath, 'rt') as f:
        while True:
            header = f.readline().strip()
            if not header:
                break
            seq = f.readline().strip()
            comment = f.readline().strip()
            qual = f.readline().strip()
            yield header, seq, comment, qual

def phred_scores(qual_str: str, phred_offset: int = 33) -> list:
    """Converts ASCII quality characters to Phred quality integers."""
    return [ord(c) - phred_offset for c in qual_str]

def mean_quality(scores: list) -> float:
    """Calculates arithmetic mean quality score."""
    return sum(scores) / len(scores) if scores else 0.0

def filter_fastq(input_path, output_path, min_q=30, min_len=50):
    total = 0
    passed = 0
    
    out_file = open(output_path, 'w') if output_path else None
    
    for header, seq, comment, qual in parse_fastq(input_path):
        total += 1
        scores = phred_scores(qual)
        avg_q = mean_quality(scores)
        
        if avg_q >= min_q and len(seq) >= min_len:
            passed += 1
            if out_file:
                out_file.write(f"{header}\n{seq}\n{comment}\n{qual}\n")
    
    if out_file:
        out_file.close()
        
    return total, passed

def main():
    parser = argparse.ArgumentParser(description="Chapter 4: FASTQ Stream Parser & Quality Filter.")
    parser.add_argument("-i", "--input", help="Path to input FASTQ file (.fastq or .fastq.gz)")
    parser.add_argument("-o", "--output", help="Optional output path for filtered FASTQ")
    parser.add_argument("-q", "--min-q", type=float, default=30.0, help="Minimum average Phred quality score (Default: 30)")
    parser.add_argument("-l", "--min-len", type=int, default=50, help="Minimum read length (Default: 50)")
    args = parser.parse_args()

    if not args.input:
        # Create a synthetic mini FASTQ file to demo execution
        demo_file = "/tmp/demo_reads.fastq"
        with open(demo_file, "w") as f:
            f.write("@READ_001_HIGH_QUAL\nACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGT\n+\nIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII\n")
            f.write("@READ_002_LOW_QUAL\nNNNNACGTACGTACGTACGTACGTACGTACGTACGTACGTACGTACGT\n+\n######IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII\n")
            f.write("@READ_003_SHORT\nACGTACGT\n+\nIIIIIIII\n")
        print(f"[!] No input specified. Created demo FASTQ with 3 reads at {demo_file}\n")
        args.input = demo_file

    total, passed = filter_fastq(args.input, args.output, min_q=args.min_q, min_len=args.min_len)
    pass_pct = (passed / total * 100) if total > 0 else 0
    print(f"=== FASTQ Quality Filtering Summary ===")
    print(f"Total Reads Analyzed: {total}")
    print(f"Passed Reads (Q>={args.min_q}, Len>={args.min_len}): {passed} ({pass_pct:.2f}%)")
    print(f"Filtered Out Reads  : {total - passed}")

if __name__ == "__main__":
    main()
