"""Code to handle files regarding compression and decompression."""

import datetime
import logging
from pathlib import Path

from housekeeper.store.models import File, Version

from cg.constants import HK_FASTQ_TAGS, SequencingFileTag
from cg.constants.compression import (
    FASTQ_DATETIME_DELTA,
    FASTQ_FIRST_READ_SUFFIX,
    FASTQ_SECOND_READ_SUFFIX,
)
from cg.models.compression_data import CompressionData

LOG = logging.getLogger(__name__)

# Functions to get common files


def get_hk_files_dict(tags: list[str], version_obj: Version) -> dict[Path, File]:
    """Fetch files from a version in Housekeeper."""
    hk_file: dict[Path, File] = {}
    tags: set = set(tags)
    for version_file in version_obj.files:
        file_tags: set[str] = {tag.name for tag in version_file.tags}
        if not file_tags.intersection(tags):
            continue
        LOG.debug(f"Found file {version_file.path}")
        path_obj: Path = Path(version_file.full_path)
        hk_file[path_obj] = version_file
    return hk_file


def is_file_in_version(version_obj: Version, path: Path) -> bool:
    """Check if a file is in a certain version considering relative path in the database."""
    return any(version_file.path in path.as_posix() for version_file in version_obj.files)


# Functions to get FASTQ like files


def get_spring_paths(version_obj: Version) -> list[CompressionData]:
    """Get all SPRING paths for a sample."""
    hk_files_dict: dict[Path, File] = get_hk_files_dict(
        tags=[SequencingFileTag.SPRING], version_obj=version_obj
    )
    spring_paths: list = []

    if hk_files_dict is None:
        return spring_paths

    spring_paths.extend(CompressionData(file_path.with_suffix("")) for file_path in hk_files_dict)
    return spring_paths


def get_fastq_stub(fastq_path: Path) -> Path:
    """Take a FASTQ file and return the stub (unique part).

    Example:
        fastq_file = /home/fastq_files/A_sequencing_run_R1_001.fastq.gz
        file_prefix = /home/fastq_files/A_sequencing_run
    """
    fastq_string: str = str(fastq_path)
    if FASTQ_FIRST_READ_SUFFIX in fastq_string:
        return Path(fastq_string.replace(FASTQ_FIRST_READ_SUFFIX, ""))
    if FASTQ_SECOND_READ_SUFFIX in fastq_string:
        return Path(fastq_string.replace(FASTQ_SECOND_READ_SUFFIX, ""))
    return None


def get_compression_data(fastq_files: list[Path]) -> list[CompressionData]:
    """Return a list of compression data objects.

    Each object has information about a pair of FASTQ files from the same run.
    """
    fastq_runs: set = set()
    compressions: list[CompressionData] = []
    for fastq_file in fastq_files:
        # file prefix is the run name identifier
        file_prefix: Path | None = get_fastq_stub(fastq_file)
        if file_prefix is None:
            LOG.info(f"Invalid FASTQ name {fastq_file}")
            continue
        run_name: str = str(file_prefix)
        if run_name not in fastq_runs:
            fastq_runs.add(run_name)
            compressions.append(CompressionData(file_prefix))
    return compressions


def get_fastq_files(sample_id: str, version_obj: Version) -> dict[str, dict]:
    """Get FASTQ files for sample."""
    hk_files_dict = get_hk_files_dict(tags=HK_FASTQ_TAGS, version_obj=version_obj)
    if hk_files_dict is None:
        return None

    fastq_dict = {}
    compression_objects: list[CompressionData] = get_compression_data(list(hk_files_dict.keys()))
    if not compression_objects:
        LOG.info(f"Could not find FASTQ files for {sample_id}")
        return None

    for compression_obj in compression_objects:
        if not check_fastqs(compression_obj):
            LOG.info(f"Skipping run {compression_obj.run_name}")
            continue
        fastq_dict[compression_obj.run_name] = {
            "compression_data": compression_obj,
            "hk_first": hk_files_dict[compression_obj.fastq_first],
            "hk_second": hk_files_dict[compression_obj.fastq_second],
        }

    return fastq_dict


def check_fastqs(compression_obj: CompressionData) -> bool:
    """Check if FASTQ files has the correct status.

    More specific this means to check
        - Did we get the full path of the file?
        - Does the file exist?
        - Do we have permissions?
        - Is the file actually a symlink?
        - Is the file a hardlink?
        - Is the file older than the specified time delta?
    """
    if not (
        compression_obj.is_absolute(compression_obj.fastq_first)
        or compression_obj.is_absolute(compression_obj.fastq_second)
    ):
        return False

    if not compression_obj.pair_exists():
        return False

    date_changed = compression_obj.get_change_date(compression_obj.fastq_first)
    today = datetime.datetime.now()

    # Check if date is older than FASTQ_DELTA
    if date_changed > today - FASTQ_DATETIME_DELTA:
        LOG.info("FASTQ files are not old enough")
        return False
    return True
