import datetime
from pathlib import Path
from typing import Any, Dict

import pandas as pd
from metacarp import version
from metacarp.camel_gene_detection.app.components.html.htmlcitation import HtmlCitation
from metacarp.camel_gene_detection.app.components.html.htmlreportsection import HtmlReportSection
from metacarp.camel_gene_detection.app.components.html.htmltablecell import HtmlTableCell

def create_analysis_info_section(config_: Dict[str, Any]) -> HtmlReportSection:
    """
    Creates the analysis info section.
    :param config_: Configuration data
    :return: Analysis info section
    """
    section = HtmlReportSection('Analysis info')

    # Analysis info
    section.add_table([
        ['Workflow version:', version.__version__],
        ['Analysis date:', datetime.datetime.now().strftime('%d/%m/%Y - %X')],
        ['Nb. of samples:', str(len(config_['input'].keys()))],
    ], table_attributes=[('class', 'information')])

    # Citation disclaimer
    section.add_header('Disclaimer', 2)
    section.add_paragraph('If you use this pipeline for your scientific work, please cite:')
    section.add_html_object(HtmlCitation.parse_from_json('Orval_2025-vitamins'))
    return section

def create_parameter_section(usual_suspects: Path, config_: Dict[str, Any]) -> HtmlReportSection:
    """
    Creates the parameter section.
    :param config_: Configuration data
    :return: Section
    """
    section = HtmlReportSection('Parameters')
    usual_suspects = pd.read_table(usual_suspects)
    list_suspects = ', '.join(usual_suspects.species.tolist())
    section.add_paragraph(f'The following usual suspects were included in this screening: {list_suspects}')
    section.add_table([
        ['Cutoff unclassified reads <small>(--cutoff-unclassified)</small>', f"{config_['filters']['cutoff_unclassified']:.2f}%"],
        ['Cutoff allergens <small>(--cutoff-allergens)</small>', f"{config_['filters']['cutoff_allergens']:.2f}%"],
        ['Cutoff prokaryotes/fungi <small>(--cutoff-prok-fungi)</small>', f"{config_['filters']['cutoff_prok_fungi']:.2f}%"],
        ['Cutoff eukaryotes <small>(--cutoff-euk)</small>', f"{config_['filters']['cutoff_euk']:.2f}%"],
    ], column_names=['Parameter', 'Value'], table_attributes=[('class', 'data')])
    return section

def create_confidence_cutoff_section(confidence_cutoffs: dict) -> HtmlReportSection:
    """
        Creates the table with confidence cutoffs.
        :param confidence_cutoffs: dictionary with confidence cutoffs
        :return: Section
        """
    section = HtmlReportSection('Confidence cutoffs')
    section.add_table([
        ['Prokaryotes', 'breadth of coverage (%)', f"{confidence_cutoffs['ilmn']['prok']['hc']:.3f}%",
         f"{confidence_cutoffs['ilmn']['prok']['lc']:.3f}%", f"{confidence_cutoffs['ont']['prok']['hc']:.3f}%",
         f"{confidence_cutoffs['ont']['prok']['lc']:.3f}%"],
        ['Fungi', 'breadth of coverage (%)', f"{confidence_cutoffs['ilmn']['fungi']['hc']:.3f}%",
         f"{confidence_cutoffs['ilmn']['fungi']['lc']:.3f}%", f"{confidence_cutoffs['ont']['fungi']['hc']:.3f}%",
         f"{confidence_cutoffs['ont']['fungi']['lc']:.3f}%"],
        ['Other eukaryotes', 'number of mapped reads', f"{confidence_cutoffs['ilmn']['euk']['hc']:,}",
         f"{confidence_cutoffs['ilmn']['euk']['lc']:,}", f"{confidence_cutoffs['ont']['euk']['hc']:,}",
         f"{confidence_cutoffs['ont']['euk']['lc']:,}"]
    ], column_names=['Group', 'Metric', 'High confidence (Illumina)', 'Low confidence (Illumina)',
                     'High confidence (ONT)', 'Low confidence (ONT)'], table_attributes=[('class', 'data')])
    return section

def __get_colored_cell_abundance(value: float, cutoff: float) -> HtmlTableCell:
    """
    Returns a colored cell for the unclassified report.
    :param value: Abundance value
    :return: Table cell
    """
    value_str = f'{value:.2f}%'
    if value >= cutoff:
        color = 'orange'
    else:
        color = 'green'
    return HtmlTableCell(value_str, color=color)

def __get_formatted_cell(value) -> HtmlTableCell:
    """
        Returns a formatted cell for the mapping output.
        :param value: Depth or reads value (can become > 1,000)
        :return: Table cell
        """
    value_str = f'{value:,}'
    color = None
    return HtmlTableCell(value_str, color=color)

def create_unclassified_section(path_stats: Path, cutoff_unclassified: float) -> HtmlReportSection:
    """
    Creates the section with output of screening for unclassified reads.
    :param path_stats: Path to the TSV stats file
    :param cutoff_unclassified: cutoff for warning unclassified reads
    :return: Section
    """
    section = HtmlReportSection('Unclassified reads')
    try:
        data = pd.read_table(path_stats)
        header = ['Sample', 'Percentage', 'Number of reads']
        section.add_table([[
            row['sample'],
            __get_colored_cell_abundance(row['abundance'], cutoff_unclassified),
            __get_formatted_cell(row['reads']),
        ] for row in data.to_dict('records')], header, [('class', 'data')])
        section.add_paragraph(
        """
        Samples with a % unclassified reads above the specified cutoff are displayed in orange (warning).
        """
        )
    except (FileNotFoundError, pd.errors.EmptyDataError, KeyError):
        section.add_paragraph(
            """
            No unclassified reads were detected for any of the samples
            """
        )
    return section

def create_allergens_section(allergens: Path, path_stats: Path) -> HtmlReportSection:
    """
    Creates the section with output of screening for allergens.
    :param allergens: Path to the list with allergens
    :param path_stats: Path to the TSV stats file
    :return: Section
    """
    section = HtmlReportSection('Allergens')
    allergens = pd.read_table(allergens)
    list_allergens = ', '.join(allergens.group.tolist())
    section.add_paragraph(f'The samples were screened for the following allergens: {list_allergens}')
    try:
        data = pd.read_table(path_stats)
        header = ['Sample', 'Species', 'Percentage', 'Number of reads']
        section.add_table([[
            row['sample'],
            row['species'],
            row['abundance'],
            __get_formatted_cell(row['reads']),
        ] for row in data.to_dict('records')], header, [('class', 'data')])
        section.add_paragraph(
        """
        If no result is shown for a sample, no allergens were detected above the given threshold (see parameters above).
        """
        )
    #if table is empty: no allergens detected
    except (FileNotFoundError, pd.errors.EmptyDataError, KeyError):
        section.add_paragraph(
            """
            No allergens were detected above the given threshold for any of the samples.
            """
        )
    return section

def __get_colored_cell(value, cutoff) -> HtmlTableCell:
    """
    Returns a colored cell for the mapping output.
    :param value: Breadth or reads value
    :return: Table cell
    """
    value_str = f'{value:,}'
    color = 'green' if value >= cutoff else 'orange'
    return HtmlTableCell(value_str, color=color)

def create_mapping_section(path_stats: Path, confidence_cutoffs: dict) -> HtmlReportSection:
    """
    Creates the read mapping section.
    :param path_stats: Path to the TSV stats file
    :param confidence_cutoffs: Dictionary with confidence cutoffs
    :return: Section
    """
    section = HtmlReportSection('Species detection results for ' + path_stats.parent.name)
    data = pd.read_table(path_stats)
    bacteria = data[(data['group'] == 'Bacteria') & (data['breadth'] >= confidence_cutoffs['prok']['lc'])]
    fungi = data[(data['group'] == 'Fungi') & (data['breadth'] >= confidence_cutoffs['fungi']['lc'])]
    other_euk = data[(data['group'].isin(['Animals', 'Plants'])) &
                     (data['reads'] >= confidence_cutoffs['euk']['lc']) &
                     (data['breadth'] >= 0.05)] #minimal filter to avoid spurious false positives
    header = ['Species', 'Depth of coverage', 'Breadth of coverage (%)', 'Mapped reads', 'Abundance (kraken2)']
    if len(bacteria) > 0:
        section.add_paragraph('Detected prokaryotes:')
        section.add_table([[
            row['species'],
            __get_formatted_cell(row['depth']),
            __get_colored_cell(row['breadth'], confidence_cutoffs['prok']['hc']),
            __get_formatted_cell(row['reads']),
            row['abundance']
        ] for row in bacteria.to_dict('records')], header, [('class', 'data')])
    if len(fungi) > 0:
        section.add_paragraph('Detected fungi:')
        section.add_table([[
            row['species'],
            __get_formatted_cell(row['depth']),
            __get_colored_cell(row['breadth'], confidence_cutoffs['fungi']['hc']),
            __get_formatted_cell(row['reads']),
            row['abundance']
        ] for row in fungi.to_dict('records')], header, [('class', 'data')])
    if len(other_euk) > 0:
        section.add_paragraph('Other detected eukaryotes:')
        section.add_table([[
            row['species'],
            __get_formatted_cell(row['depth']),
            row['breadth'],
            __get_colored_cell(row['reads'], confidence_cutoffs['euk']['hc']),
            row['abundance']
        ] for row in other_euk.to_dict('records')], header, [('class', 'data')])
    if len(bacteria) > 0 or len(fungi) > 0 or len(other_euk) > 0:
        section.add_paragraph(
            """
            Species with a breadth of coverage (for prokaryotes and fungi) or a number of mapped reads (for eukaryotes) above the high and low confidence cutoff are displayed in green or orange, respectively. 
            """
        )
    else:
        section.add_paragraph(
        """
        No species reached the required cutoffs.
        """
        )

    return section