import os
from datetime import date, datetime
from importlib.metadata import PackageNotFoundError, version
from importlib.resources import as_file, files

import rwe.clients.aou as aou
import rwe.clients.genebass as gbs
import rwe.clients.gnomad as gnomad


_DEFAULT_TEMPLATE_RESOURCE = "assets/RWE_template.pptx"

_DEFAULT_SLIDE4_ANCHORS = {
    "aou": (306000, 1110629, 5315528, 2686727),
    "ukb": (6276035, 1114132, 5419390, 2732276),
    "survey": (599025, 4035903, 4688785, 2360422),
}


def _send_shape_to_back(slide, shape) -> None:
    sp_tree = slide.shapes._spTree
    sp = shape._element
    sp_tree.remove(sp)
    sp_tree.insert(2, sp)


def _replace_title_slide_text(slide, gene: str, indications: list[str], report_date: str) -> None:
    from pptx.util import Pt
    cleaned_indications = [indication.replace("_", " ").strip() for indication in indications if indication.strip()]
    indication_text = ", ".join(cleaned_indications) if cleaned_indications else "general"
    for shape in slide.shapes:
        if not getattr(shape, "has_text_frame", False):
            continue
        text = shape.text.strip()
        if "Real world evidence for" in text:
            text_frame = shape.text_frame
            title_font_size = Pt(60)
            text_frame.clear()
            p = text_frame.paragraphs[0]
            lead_run = p.add_run()
            lead_run.text = "Real world evidence for "
            lead_run.font.size = title_font_size
            gene_run = p.add_run()
            gene_run.text = gene
            gene_run.font.italic = True
            gene_run.font.size = title_font_size
            suffix_run = p.add_run()
            suffix_run.text = f" - {indication_text}"
            suffix_run.font.size = title_font_size
        elif "Deepro Banerjee" in text and "Arrowhead Pharmaceuticals" in text:
            shape.text = (
                "Deepro Banerjee, Shicheng Guo\n"
                f"{report_date}\n"
                "Translational Genetics and Data Science\n"
                "Arrowhead Pharmaceuticals"
            )
    return

def _extract_slide4_anchors(slide, mso_shape_type):
    picture_shapes = [s for s in slide.shapes if s.shape_type == mso_shape_type.PICTURE]
    picture_shapes = sorted(picture_shapes, key=lambda s: (s.top, s.left))
    if len(picture_shapes) >= 3:
        return {
            "aou": (picture_shapes[0].left, picture_shapes[0].top, picture_shapes[0].width, picture_shapes[0].height),
            "ukb": (picture_shapes[1].left, picture_shapes[1].top, picture_shapes[1].width, picture_shapes[1].height),
            "survey": (picture_shapes[2].left, picture_shapes[2].top, picture_shapes[2].width, picture_shapes[2].height),
        }
    return _DEFAULT_SLIDE4_ANCHORS.copy()


def _format_count_with_breakdown(values: list[tuple[str, int | None]]) -> str:
    present = [(label, value) for label, value in values if value is not None]
    if not present:
        return "NA"
    total = sum(value for _, value in present)
    if total == 0:
        return "0"
    breakdown = " + ".join(str(value) for _, value in present)
    return f"{total} ({breakdown})"


def _build_safety_bullets(gene: str, output_dir: str, include_aou: bool, include_genebass: bool) -> list[str]:
    hgnc_path = os.path.join(output_dir, "gene_with_protein_product.txt")
    constraint_path = os.path.join(output_dir, "gnomad.v4.1.constraint_metrics.tsv")

    gnomad_info = gnomad.get_gnomad_constraint_info(
        gene,
        constraint_path=constraint_path,
        hgnc_path=hgnc_path,
    )
    approved_gene = gnomad_info["gene"]
    genebass_info = gbs.get_genebass_carrier_info(approved_gene) if include_genebass else {"nhetz": None, "nhomz": None}
    aou_info = aou.get_aou_carrier_info(gene) if include_aou else {"nhetz": None, "nhomz": None}

    heterozygous = [
        ("gnomAD", gnomad_info.get("nhetz")),
        ("UK Biobank", genebass_info.get("nhetz")),
        ("All of Us", aou_info.get("nhetz")),
    ]
    homozygous = [
        ("gnomAD", gnomad_info.get("nhomz")),
        ("UK Biobank", genebass_info.get("nhomz")),
        ("All of Us", aou_info.get("nhomz")),
    ]
    included_labels = [label for label, value in heterozygous if value is not None] + [
        label for label, value in homozygous if value is not None and label not in [l for l, _ in heterozygous if _ is not None]
    ]
    if not included_labels:
        cohort_text = "gnomAD, UK Biobank and All of Us"
    elif len(included_labels) == 1:
        cohort_text = included_labels[0]
    elif len(included_labels) == 2:
        cohort_text = f"{included_labels[0]} and {included_labels[1]}"
    else:
        cohort_text = ", ".join(included_labels[:-1]) + f" and {included_labels[-1]}"

    loeuf = gnomad_info.get("loeuf")
    loeuf_text = "NA" if loeuf is None else f"{loeuf:.3f}"

    return [
        "No consistent cross-biobank toxicity signal in clinical records of predicted loss of function carriers.",
        f"LOEUF score: {loeuf_text}.",
        (
            f"{cohort_text} cohorts have {_format_count_with_breakdown(heterozygous)} heterozygous "
            f"and {_format_count_with_breakdown(homozygous)} homozygous loss of function carriers."
        ),
        "Self-reported survey information of four health related questions in loss of function carriers is similar to non-carriers.",
    ]


def _update_slide4_safety_text(slide, bullets: list[str]) -> None:
    from pptx.util import Pt

    for shape in slide.shapes:
        if not getattr(shape, "has_text_frame", False):
            continue
        text = shape.text.strip()
        if "No consistent cross-biobank toxicity signal" not in text:
            continue
        text_frame = shape.text_frame
        paragraphs = list(text_frame.paragraphs)
        while len(paragraphs) < len(bullets):
            paragraphs.append(text_frame.add_paragraph())
        for idx, bullet in enumerate(bullets):
            paragraph = paragraphs[idx]
            paragraph.text = bullet
            for run in paragraph.runs:
                run.font.size = Pt(14)
        for paragraph in paragraphs[len(bullets):]:
            paragraph.text = ""
        return


def _populate_slide4(
    slide,
    gene: str,
    output_dir: str,
    aou_plot: str | None,
    ukb_plot: str | None,
    survey_plot: str | None,
    include_aou: bool,
    include_genebass: bool,
    mso_shape_type,
) -> None:
    anchors = _extract_slide4_anchors(slide, mso_shape_type)

    for shape in list(slide.shapes):
        if shape.shape_type == mso_shape_type.PICTURE:
            shape.element.getparent().remove(shape.element)
    if aou_plot and os.path.exists(aou_plot):
        left, top, width, height = anchors["aou"]
        aou_picture = slide.shapes.add_picture(aou_plot, left, top, width=width, height=height)
        _send_shape_to_back(slide, aou_picture)
    if ukb_plot and os.path.exists(ukb_plot):
        left, top, width, height = anchors["ukb"]
        ukb_picture = slide.shapes.add_picture(ukb_plot, left, top, width=width, height=height)
        _send_shape_to_back(slide, ukb_picture)
    if survey_plot and os.path.exists(survey_plot):
        left, top, width, height = anchors["survey"]
        survey_picture = slide.shapes.add_picture(survey_plot, left, top, width=width, height=height)
        _send_shape_to_back(slide, survey_picture)
    bullets = _build_safety_bullets(gene, output_dir, include_aou, include_genebass)
    _update_slide4_safety_text(slide, bullets)
    return

def generate_rwe_presentation(
    gene: str,
    indications: str | list[str] | None = None,
    output_dir: str = "",
    report_date: str = "",
    allofus: bool = True,
    astrazeneca: bool = True,
    genebass: bool = True,
) -> str:
    """
    Generate a 6-slide RWE PPTX from the template.

    Slide behavior:
    1. Title slide: update gene, indications, and date.
    2. Keep as template.
    3. Keep as template.
    4. Clinical safety slide with AoU + UKB (AZN preferred, GeneBass fallback) Manhattan plots and AoU survey plot.
       These plots are expected to already exist in output_dir from the DOCX generation step.
    5. Keep as template.
    6. Keep as template.
    """
    try:
        from pptx import Presentation
        from pptx.enum.shapes import MSO_SHAPE_TYPE
    except ImportError as exc:
        raise ImportError(
            "python-pptx is required to generate PowerPoint files. Install with: pip install python-pptx"
        ) from exc

    if isinstance(indications, str):
        indications_list = [x for x in indications.split(",") if x]
    else:
        indications_list = indications or []
    if not report_date:
        report_date = date.today().strftime("%B %d, %Y")

    if not output_dir:
        output_dir = "./rwe_reports"
    os.makedirs(output_dir, exist_ok=True)

    date_tag = datetime.today().strftime("%m%d%y")
    indication_part = "-".join(indications_list) if indications_list else "general"
    try:
        pkg_version = version("rwe")
    except PackageNotFoundError:
        pkg_version = "unknown"
    out_pptx_path = os.path.join(
        output_dir,
        f"RWE_{gene}_{indication_part}_report_{date_tag}_v{pkg_version}.pptx",
    )

    aou_plot = None
    if allofus:
        candidate = os.path.join(output_dir, f"{gene}_aou_phewas.png")
        if os.path.exists(candidate):
            aou_plot = candidate

    ukb_plot = None
    if astrazeneca:
        candidate = os.path.join(output_dir, f"{gene}_azn_phewas.png")
        if os.path.exists(candidate):
            ukb_plot = candidate
    if ukb_plot is None and genebass:
        candidate = os.path.join(output_dir, f"{gene}_genebass_phewas.png")
        if os.path.exists(candidate):
            ukb_plot = candidate

    survey_plot = None
    if allofus:
        candidate = os.path.join(output_dir, f"{gene}_aou_surveys.png")
        if os.path.exists(candidate):
            survey_plot = candidate
        else:
            candidate = os.path.join(output_dir, f"{gene}_aou_survey.png")
            if os.path.exists(candidate):
                survey_plot = candidate

    template_resource = files("rwe").joinpath(_DEFAULT_TEMPLATE_RESOURCE)
    with as_file(template_resource) as template_fs_path:
        prs = Presentation(str(template_fs_path))

    if len(prs.slides) < 6:
        raise ValueError("Template must contain at least 6 slides.")

    _replace_title_slide_text(prs.slides[0], gene, indications_list, report_date)
    _populate_slide4(
        prs.slides[3],
        gene,
        output_dir,
        aou_plot,
        ukb_plot,
        survey_plot,
        allofus,
        genebass,
        MSO_SHAPE_TYPE,
    )

    prs.save(out_pptx_path)
    return out_pptx_path
