"""
Pre-designed guide cache module for CRISPR gRNA design.

Handles loading, filtering, and merging of pre-designed guides
to avoid redundant computation.
"""

import os
import pandas as pd


def load_predesigned_guides(predesigned_path):
    """
    Load a previously scored designGuides.txt file.

    Args:
        predesigned_path: Path to the pre-designed guides file,
                          or None/empty string if no pre-designed guides

    Returns:
        DataFrame with pre-designed guide information,
        or an empty DataFrame if path is empty/None or file doesn't exist
    """
    # Handle empty/None path
    if not predesigned_path:
        return pd.DataFrame()

    # Handle non-existent file
    if not os.path.exists(predesigned_path):
        return pd.DataFrame()

    # Load the file
    try:
        df = pd.read_csv(predesigned_path, sep='\t')
        return df
    except Exception:
        return pd.DataFrame()


def filter_predesigned_regions(regions_df, predesigned_df):
    """
    Given a DataFrame of input regions and a DataFrame of pre-designed guides,
    return only the regions that have no pre-designed guides covering them.

    A region is considered covered if at least one guide's guideSet matches
    the region name.

    Args:
        regions_df: DataFrame with region information (must have 'name' column
                    or will use 'chr:start-end' as identifier)
        predesigned_df: DataFrame of pre-designed guides (must have 'guideSet' column)

    Returns:
        DataFrame of regions that need new guide design
    """
    if predesigned_df.empty:
        return regions_df.copy()

    if regions_df.empty:
        return regions_df.copy()

    # Get set of covered region names from pre-designed guides
    covered_regions = set()
    if 'guideSet' in predesigned_df.columns:
        covered_regions = set(predesigned_df['guideSet'].unique())

    # Determine region name column
    if 'name' in regions_df.columns:
        name_col = 'name'
    elif 'region_name' in regions_df.columns:
        name_col = 'region_name'
    else:
        # Create region name from coordinates
        regions_df = regions_df.copy()
        regions_df['_region_name'] = (
            regions_df['chr'].astype(str) + ':' +
            regions_df['start'].astype(str) + '-' +
            regions_df['end'].astype(str)
        )
        name_col = '_region_name'

    # Filter out covered regions
    mask = ~regions_df[name_col].isin(covered_regions)
    result = regions_df[mask].copy()

    # Remove temporary column if we created it
    if '_region_name' in result.columns:
        result = result.drop(columns=['_region_name'])

    return result


def merge_with_predesigned(new_guides_df, predesigned_df):
    """
    Concatenate new guides with pre-designed guides and return the combined
    DataFrame, deduplicating on (chr, start, end, strand).

    Args:
        new_guides_df: DataFrame of newly designed guides
        predesigned_df: DataFrame of pre-designed guides

    Returns:
        Combined DataFrame with duplicates removed (keeping first occurrence,
        which prioritizes new guides)
    """
    if predesigned_df.empty:
        return new_guides_df.copy()

    if new_guides_df.empty:
        return predesigned_df.copy()

    # Concatenate DataFrames (new guides first to prioritize them in dedup)
    combined = pd.concat([new_guides_df, predesigned_df], ignore_index=True)

    # Deduplicate on key columns
    dedup_columns = ['chr', 'start', 'end', 'strand']

    # Verify all dedup columns exist
    existing_dedup_cols = [col for col in dedup_columns if col in combined.columns]

    if existing_dedup_cols:
        # Keep first occurrence (new guides take priority)
        combined = combined.drop_duplicates(subset=existing_dedup_cols, keep='first')

    return combined.reset_index(drop=True)


def get_covered_guide_sets(predesigned_df):
    """
    Get the set of guideSet values that are covered by pre-designed guides.

    Args:
        predesigned_df: DataFrame of pre-designed guides

    Returns:
        Set of guideSet values
    """
    if predesigned_df.empty or 'guideSet' not in predesigned_df.columns:
        return set()

    return set(predesigned_df['guideSet'].unique())


def count_guides_per_region(guides_df):
    """
    Count the number of guides per region (guideSet).

    Args:
        guides_df: DataFrame of guides with 'guideSet' column

    Returns:
        Dictionary mapping guideSet to guide count
    """
    if guides_df.empty or 'guideSet' not in guides_df.columns:
        return {}

    return guides_df.groupby('guideSet').size().to_dict()
