"""Helper classes to define the data input for the qc report.

Copyright © 2022 Pixelgen Technologies AB.
"""

import dataclasses
from typing import List, Optional, TypedDict, TypeVar

from pixelator.pna.report.models.commands import CommandInfo

CommandOptionValue = TypeVar("CommandOptionValue", str, int, float, bool)


@dataclasses.dataclass
class SampleInfo:
    """Dataclass for general sample information.

    The values defined here are used in the dynamic qc report to populate the
    "sample information" panel.
    """

    #: Version of pixelator used to generate the metrics
    pixelator_version: str

    #: Date the report was generated
    generation_date: str

    #: Identifier of the sample
    sample_id: str

    #: Short description of the sample
    sample_description: str

    #: Name of the pixelator panel file
    panel_name: Optional[str]

    #: Version of the pixelator panel file
    panel_version: Optional[str]

    #: Name of the technology used
    technology: Optional[str]

    #: Version of the technology used
    technology_version: Optional[str]

    #: List of commands and their parameters
    parameters: List[CommandInfo]


# --- START AUTOGENERATED: metrics ---
class Metrics(TypedDict, total=False):
    """A typed dictionary containing all metrics that can be used in the qc report.

    Values defined here will be used in the dynamic qc report to populate
    the different data tables. Missing values will be left blank.

    Note: This class is autogenerated from the metrics.json file (0.1.0)
          in the pixelator-qc-report-pna package.
    """

    #: Total number of read pairs that were input to this library.
    number_of_reads: int

    #: Fraction of total input reads discarded by the pipeline over all steps.
    fraction_discarded_reads: float

    #: Number of read pairs that were input to the amplicon assembly step.
    input_reads_amplicon: int

    #: Number of read pairs that were output from the amplicon assembly step.
    output_reads_amplicon: int

    #: Number of reads discarded during the amplicon assembly step.
    discarded_reads_amplicon: int

    #: Percentage of the total input reads discarded during the amplicon step. Reads
    #: can be discarded by amplicon when the (partial) LBS-1 and LBS-2 regions cannot
    #: be recognised.
    fraction_discarded_reads_amplicon: float

    #: Number of read pairs that were input to the demux step.
    input_reads_demux: int

    #: Number of reads that were error-corrected during the demux step.
    corrected_reads_demux: float

    #: Number of read pairs that were output from the demux step.
    output_reads_demux: int

    #: Percentage of reads discarded during the demux step. Reads can be discarded by
    #: demux when the PID1 and PID2 regions do not match the markers from the panel
    #: and cannot be corrected unambiguously.
    discarded_reads_demux: float

    #: Fraction of total input reads discarded by demux because of invalid marker
    #: barcodes.
    fraction_discarded_reads_demux: float

    #: Number of read pairs that were the input to the collapse step.
    input_reads_collapse: int

    #: Number of reads that were the output from the collapse step.
    output_reads_collapse: int

    #: Number of unique molecules that were the output from the collapse step.
    output_molecules_collapse: int

    #: The total number of Reads that were recovered by error correction in the
    #: collapse step.
    corrected_reads_collapse: float

    #: Fraction of total input reads with invalid PID1 & PID2 after error-correction.
    fraction_of_molecules_corrected_collapse: float

    #: Number of read pairs that were the input to the graph step.
    input_reads_graph: int

    #: Number of read pairs that were included in the final graph.
    output_reads_graph: int

    #: Number of molecules that were the input to the graph step.
    input_molecules_graph: int

    #: Number of molecules that were included in the final graph.
    output_molecules_graph: int

    #: Number of molecules discarded during the graph step.
    discarded_reads_graph: int

    #: Number of molecules discarded during the graph step.
    discarded_molecules_graph: int

    #: Fraction of total input reads discarded during the graph step.
    fraction_discarded_reads_graph: float

    #: The redundancy of valid reads. Valid reads are reads that have passed the
    #: demux step. The saturation is calculated as: <br /> $$ 1- rac{	ext{\#
    #: unique valid reads}}{	ext{\# total valid reads}} $$
    valid_reads_saturation: float

    #: The redundancy of edge detection. The saturation is calculated as: <br /> $$ 1
    #: - rac{	ext{\# graph edges}}{	ext{\# graph reads}} $$
    graph_edge_saturation: float

    #: The redundancy of node detection. The saturation is calculated as: <br /> $$ 1
    #: - rac{	ext{\# graph nodes}}{ 	ext{\# graph reads}} $$
    graph_node_saturation: float

    #: Percentage of total input reads that are valid.
    fraction_valid_reads: float

    #: Percentage of total input reads in valid components.
    fraction_graph_reads: float

    #: Fraction of bases in a full amplicon with Q-score ≥ 30, excluding very low
    #: quality/no-call (Q ≤ 2) bases from the denominator.
    fraction_q30_bases: float

    #: Fraction of bases in UMI1 with Q-score ≥ 30, excluding very low quality/no-
    #: call (Q ≤ 2) bases from the denominator.
    fraction_q30_bases_in_umi1: float

    #: Fraction of bases in UMI2 with Q-score ≥ 30, excluding very low quality/no-
    #: call (Q ≤ 2) bases from the denominator.
    fraction_q30_bases_in_umi2: float

    #: Fraction of bases in PID1 with Q-score ≥ 30, excluding very low quality/no-
    #: call (Q ≤ 2) bases from the denominator.
    fraction_q30_bases_in_pid1: float

    #: Fraction of bases in PID2 with Q-score ≥ 30, excluding very low quality/no-
    #: call (Q ≤ 2) bases from the denominator.
    fraction_q30_bases_in_pid2: float

    #: Fraction of bases in UEI with Q-score ≥ 30, excluding very low quality/no-call
    #: (Q ≤ 2) bases from the denominator.
    fraction_q30_bases_in_uei: float

    #: Fraction of bases in LBS1 with Q-score ≥ 30, excluding very low quality/no-
    #: call (Q ≤ 2) bases from the denominator.
    fraction_q30_bases_in_lbs1: float

    #: Fraction of bases in LBS2 with Q-score ≥ 30, excluding very low quality/no-
    #: call (Q ≤ 2) bases from the denominator.
    fraction_q30_bases_in_lbs2: float

    #: The number of cells annotated by the pipeline.
    number_of_cells: int

    #: Median of the number of usable reads per cell.
    median_reads_per_cell: float

    #: Median of the total number of markers per cell.
    median_markers_per_cell: float

    #: Connectivity expressed as the median of the average k-coreness of the cells.
    median_average_k_coreness: float

    #: Fraction of the total input reads that are in cells.
    fraction_discarded_cells: float

    #: The fraction of cells that are outliers as determined by the tau-metric
    #: outlier detection.
    fraction_of_outlier_cells: float


# --- END AUTOGENERATED: metrics ---


class InfoAndMetrics(TypedDict):
    """Aggregated info and metrics."""

    info: SampleInfo
    metrics: Metrics


@dataclasses.dataclass
class QCReportData:
    """Collection of data inputs for metrics and dynamic figures and tables in the qc report.

    All attributes are strings containing csv or json data ready to be
    injected into the dynamic report.
    """

    metrics: Metrics

    #: CSV formatted string containing a DataFrame for the
    #: "component_size_distribution" figure
    #: component_size, rank, selected, markers
    #: 18463, 1, True, 32
    #: 18290, 2, True, 39
    ranked_component_size: Optional[str]

    #: CSV formatted string containing a DataFrame used for plotting embeddings
    #: with various colors,
    #: eg (cell type, leiden cluster, umis)
    #: Following columns are required:
    #:  component,umap1,umap2,umis,cluster,cluster_cell_class
    #: For example:
    #: ```
    #: component,umap1,umap2,cluster,cluster_cell_class,reads,molecules,mean_upia_degree,mean_umi_per_upia
    #: 0,11.891996,12.216717,4793,2,other
    #: 1,11.02211,12.486254,6708,2,other
    #: 2,7.8869944,7.5836325,5031,5,predict_B-cells
    #: 3,0.66538113,4.00765,3446,9,predict_CD4+_T-cells
    #: 4,13.21712,5.2930307,6704,1,predict_B-cells
    #: ```
    component_data: Optional[str]

    #: CSV formatted string containing a DataFrame used to generate the antibodies
    #: per cell figure.
    #: Following columns are required: antibody,percentage
    #: For example:
    #: ```
    #: antibody,percentage
    #: CD274,0.0016104640666519112
    #: CD44,0.04755881148351282
    #: CD56,0.020829214318265954
    #: ```
    antibody_percentages: Optional[str]

    #: CSV formatted string containing a DataFrame used to generate the antibody
    #: counts violin plots.
    #: It consists of the component column followed by a list of antibody columns.
    #: For example:
    #: ```
    #: component,CD274,CD44,CD56
    #: PXLCMP0000000,9.0,76.0,63.0
    #: PXLCMP0000002,3.0,90.0,166.0
    #: PXLCMP0000003,1.0,44.0,113.0
    #: ```
    antibody_counts: Optional[str]

    #: CSV formatted string containing a DataFrame used to generate proximity heatmaps
    #:
    #: Following columns are required:
    #:  marker_1,marker_2,join_count_z,log2ratio
    #:
    #: For example:
    #: ```
    #: marker_1,marker_2,join_count_z,log2ratio
    #: CD45,HLA-ABC,0.02165450650176937,0.008440362800652843
    #: HLA-ABC,HLA-ABC,0.20508868569666797,0.010748605487355567
    #: HLA-ABC,TCRab,-0.289434564966545,-0.28095631383105635
    #: ACTB,CD45,0.0,0.0
    #: ACTB,HLA-ABC,0.23253978095569725,0.21996568394190796
    #: CD3e,CD3e,0.7555555555555555,0.6844981742720706
    proximity_data: Optional[str]

    def to_dict(self) -> dict[str, Optional[str]]:
        """Convert the dataclass to a dictionary."""
        return dataclasses.asdict(self)
