import logging
import re

from housekeeper.store.models import File, Version

from cg.apps.lims import LimsAPI
from cg.apps.madeline.api import MadelineAPI
from cg.constants.constants import FileFormat
from cg.constants.housekeeper_tags import HK_DELIVERY_REPORT_TAG, AnalysisTag, NFAnalysisTags
from cg.constants.scout import (
    RANK_MODEL_THRESHOLD,
    RAREDISEASE_CASE_TAGS,
    RAREDISEASE_SAMPLE_TAGS,
    GenomeBuild,
    UploadTrack,
)
from cg.constants.sequencing import Variants
from cg.io.controller import ReadFile
from cg.meta.upload.scout.hk_tags import CaseTags, SampleTags
from cg.meta.upload.scout.scout_config_builder import ScoutConfigBuilder
from cg.meta.workflow.raredisease import RarediseaseAnalysisAPI
from cg.models.scout.scout_load_config import (
    CaseImages,
    CustomImages,
    Eklipse,
    RarediseaseLoadConfig,
    ScoutRarediseaseIndividual,
)
from cg.store.models import Analysis

LOG = logging.getLogger(__name__)


class RarediseaseConfigBuilder(ScoutConfigBuilder):
    def __init__(
        self,
        raredisease_analysis_api: RarediseaseAnalysisAPI,
        lims_api: LimsAPI,
        madeline_api: MadelineAPI,
    ):
        super().__init__(
            lims_api=lims_api,
        )
        self.case_tags: CaseTags = CaseTags(**RAREDISEASE_CASE_TAGS)
        self.sample_tags: SampleTags = SampleTags(**RAREDISEASE_SAMPLE_TAGS)
        self.raredisease_analysis_api: RarediseaseAnalysisAPI = raredisease_analysis_api
        self.lims_api: LimsAPI = lims_api
        self.madeline_api: MadelineAPI = madeline_api

    def build_load_config(self, hk_version: Version, analysis: Analysis) -> RarediseaseLoadConfig:
        """Create a RAREDISEASE specific load config for uploading analysis to Scout."""
        LOG.info("Build load config for RAREDISEASE case")
        load_config: RarediseaseLoadConfig = RarediseaseLoadConfig(
            track=UploadTrack.RARE_DISEASE.value,
            delivery_report=self.get_file_from_hk(
                hk_tags={HK_DELIVERY_REPORT_TAG}, hk_version=hk_version
            ),
        )
        self.add_common_info_to_load_config(load_config=load_config, analysis=analysis)
        load_config.gene_panels = self.raredisease_analysis_api.get_aggregated_panels(
            customer_id=analysis.case.customer.internal_id,
            default_panels=set(analysis.case.panels),
        )
        self.include_case_files(load_config=load_config, hk_version=hk_version)
        self.get_sample_information(
            load_config=load_config, analysis=analysis, hk_version=hk_version
        )
        self.include_pedigree_picture(load_config=load_config, analysis=analysis)
        self.load_custom_image_sample(
            load_config=load_config, analysis=analysis, hk_version=hk_version
        )
        load_config.human_genome_build = GenomeBuild.hg19
        load_config.rank_score_threshold = RANK_MODEL_THRESHOLD
        load_config.rank_model_version = self.get_rank_model_version(
            variant_type=Variants.SNV, hk_version=hk_version
        )
        load_config.sv_rank_model_version = self.get_rank_model_version(
            variant_type=Variants.SV, hk_version=hk_version
        )
        return load_config

    def get_rank_model_version(self, variant_type: Variants, hk_version: Version) -> str:
        """
        Returns the rank model version for a variant type from the manifest file.
        Raises:
            FileNotFoundError if no manifest file is found in housekeeper.
        """
        hk_manifest_file: File = self.get_file_from_hk(
            {NFAnalysisTags.MANIFEST}, hk_version=hk_version
        )
        if not hk_manifest_file:
            raise FileNotFoundError("No manifest file found in Housekeeper.")
        return self.extract_rank_model_from_manifest(
            hk_manifest_file=hk_manifest_file, variant_type=variant_type
        )

    def extract_rank_model_from_manifest(
        self, hk_manifest_file: File, variant_type: Variants
    ) -> str:
        content: dict[str, dict[str, str]] = ReadFile.get_content_from_file(
            file_format=FileFormat.JSON, file_path=hk_manifest_file
        )
        return self.get_rank_model_version_from_manifest_content(
            content=content, variant_type=variant_type
        )

    def get_rank_model_version_from_manifest_content(
        self, content: dict[str, dict[str, str]], variant_type: Variants
    ) -> str:
        """
        Return the rank model version from the manifest file content.
        Raises:
            ValueError if pattern not found ing process or clinical not found in script.
        """
        pattern: str = variant_type.upper() + ":GENMOD_SCORE"
        for key, value in content["tasks"].items():
            process: str = value.get("process")
            script: str = value.get("script")
            if pattern in process and AnalysisTag.CLINICAL in script:
                return self._get_version_from_manifest_script(script)
        raise ValueError(
            f"Either {pattern} not found in any process or {AnalysisTag.CLINICAL} not found in any script of the manifest file"
        )

    def _get_version_from_manifest_script(self, script: str) -> str:
        """
        Returns the rank model version in the format 'vX.X from the given script string.
        Raises a ValueError if no rank model version is found in the script string.
        """
        if match := re.search(r"v(\d+\.\d+)", script):
            return match.group(1)
        raise ValueError("No rank model version found")

    def load_custom_image_sample(
        self, load_config: RarediseaseLoadConfig, analysis: Analysis, hk_version: Version
    ) -> None:
        """Build custom images config."""
        LOG.info("Adding custom images")

        eklipse_images: list = []
        for sample in analysis.case.samples:
            sample_id: str = sample.internal_id
            eklipse_image_path = self.get_file_from_hk(
                hk_tags=set(self.sample_tags.eklipse_path).union({sample_id}),
                hk_version=hk_version,
            )
            if eklipse_image_path:
                eklipse_image = Eklipse(
                    title=sample_id,
                    path=eklipse_image_path,
                    description="eKLIPse MT images",
                    width="800",
                    height="800",
                )
                eklipse_images.append(eklipse_image)
        if eklipse_images:
            case_images = CaseImages(eKLIPse=eklipse_images)
            config_custom_images = CustomImages(case_images=case_images)
            load_config.custom_images = config_custom_images

    def include_case_files(self, load_config: RarediseaseLoadConfig, hk_version: Version) -> None:
        """Include case level files for RAREDISEASE case."""
        LOG.info("Including RAREDISEASE specific case level files")
        for scout_key in RAREDISEASE_CASE_TAGS.keys():
            self._include_case_file(
                load_config=load_config, scout_key=scout_key, hk_version=hk_version
            )

    def _include_case_file(
        self, load_config: RarediseaseLoadConfig, scout_key: str, hk_version: Version
    ) -> None:
        """Include the file path associated to a scout configuration parameter if the corresponding housekeeper tags
        are found. Returns None."""
        file_path = self.get_file_from_hk(getattr(self.case_tags, scout_key), hk_version=hk_version)
        setattr(load_config, scout_key, file_path)

    def include_sample_files(
        self, config_sample: ScoutRarediseaseIndividual, hk_version: Version
    ) -> None:
        """Include sample level files that are optional for mip samples."""
        LOG.info("Including RAREDISEASE specific sample level files")
        sample_id: str = config_sample.sample_id
        config_sample.vcf2cytosure = self.get_sample_file(
            hk_tags=self.sample_tags.vcf2cytosure,
            sample_id=sample_id,
            hk_version=hk_version,
        )
        config_sample.mt_bam = self.get_sample_file(
            hk_tags=self.sample_tags.mt_bam, sample_id=sample_id, hk_version=hk_version
        )
        config_sample.chromograph_images.autozygous = self.remove_chromosome_substring(
            file_path=self.get_sample_file(
                hk_tags=self.sample_tags.chromograph_autozyg,
                sample_id=sample_id,
                hk_version=hk_version,
            )
        )
        config_sample.chromograph_images.coverage = self.remove_chromosome_substring(
            file_path=self.get_sample_file(
                hk_tags=self.sample_tags.chromograph_coverage,
                sample_id=sample_id,
                hk_version=hk_version,
            )
        )
        config_sample.chromograph_images.upd_regions = self.remove_chromosome_substring(
            file_path=self.get_sample_file(
                hk_tags=self.sample_tags.chromograph_regions,
                sample_id=sample_id,
                hk_version=hk_version,
            )
        )
        config_sample.chromograph_images.upd_sites = self.remove_chromosome_substring(
            file_path=self.get_sample_file(
                hk_tags=self.sample_tags.chromograph_sites,
                sample_id=sample_id,
                hk_version=hk_version,
            )
        )
        config_sample.mitodel_file = self.get_sample_file(
            hk_tags=self.sample_tags.mitodel_file,
            sample_id=sample_id,
            hk_version=hk_version,
        )
        self.include_reviewer_files(config_sample=config_sample, hk_version=hk_version)
