"""
Optional helpers: map Abromics domain objects to ENA-style field dicts.

Used when building or validating metadata alongside madbot_ena_plugin; the
canonical submission path may also map in abromics-analysis.
"""

from typing import Any, Dict, Optional



class AbromicsToENAMapper:
    """
    Maps Abromics sample metadata to ENA metadata fields.
    
    This mapper handles the conversion of Abromics-specific data structures
    to ENA-compliant metadata format.
    """
    
    @staticmethod
    def map_sample_to_ena(abromics_sample: Dict[str, Any]) -> Dict[str, Any]:
        """
        Map an Abromics sample to ENA sample metadata.
        
        Parameters
        ----------
        abromics_sample : dict
            Abromics sample data containing fields like:
            - original_sample_id
            - taxonomy
            - collected_date
            - country
            - host
            - sources
            etc.
            
        Returns
        -------
        dict
            ENA-formatted sample metadata
        """
        ena_sample: Dict[str, Any] = {}
        
        sample_id = abromics_sample.get("original_sample_id") or abromics_sample.get("strain_id")
        
        if sample_id:
            ena_sample["sample_title"] = sample_id
            ena_sample["sample_alias"] = (
                f"abromics_sample_{abromics_sample.get('id', 'unknown')}"
            )

        # sample_taxon (mandatory for ENA) - taxonomy with ncbi_id
        taxonomy = abromics_sample.get('taxonomy', {})
        if taxonomy:
            ncbi_id = taxonomy.get('ncbi_id') or taxonomy.get('id')
            if ncbi_id:
                ena_sample['sample_taxon'] = {'taxonID': str(ncbi_id)}

        # isolate = Microorganism Scientific Name (taxonomy.scientific_name)
        microorganism_scientific_name = taxonomy.get('scientific_name', '') if taxonomy else ''
        if microorganism_scientific_name:
            ena_sample["isolate"] = microorganism_scientific_name
                
        # Collection date (ERC000028 expects an object with status + value)
        collected_date = abromics_sample.get("collected_date")
        if collected_date:
            ena_sample["collection_date"] = {
                "status": "Value available",
                "value": collected_date,
            }
        
        # Geographic location - ERC000028 has specific fields
        country = abromics_sample.get('country', {})
        if country:
            country_name = country.get('name', '')
            if country_name:
                ena_sample['geographic_location_country_and_or_sea'] = country_name
            
            # Region and locality
            country_level1 = abromics_sample.get('country_level1', {})
            place = abromics_sample.get('place', {})
            region_parts = []
            if country_level1 and country_level1.get('name'):
                region_parts.append(country_level1.get('name'))
            if place and place.get('name'):
                region_parts.append(place.get('name'))
            if region_parts:
                ena_sample['geographic_location_region_and_locality'] = ', '.join(region_parts)
        
        # Host information - ERC000028 has host_scientific_name and host_health_state
        host = abromics_sample.get('host', {})
        if host:
            host_scientific = host.get('scientific_name', host.get('species', ''))
            if host_scientific:
                ena_sample['host_scientific_name'] = host_scientific
        
        # host_health_state is mandatory but not available in Abromics data, set to "not provided"
        # This field is required by ERC000028, so we must provide a value
        ena_sample['host_health_state'] = 'not provided'
        
        # Sample sources - ERC000028 has isolation_source
        # isolation_source in ENA = Sample source in Abromics (sources[0].name)
        sources = abromics_sample.get('sources', [])
        if sources and len(sources) > 0:
            # Primary source
            primary_source = sources[0]
            isolation_source_name = primary_source.get('name', '')
            if isolation_source_name:
                ena_sample['isolation_source'] = isolation_source_name
        
        
        return ena_sample
    
    @staticmethod
    def map_experiment_to_study(
        abromics_experiment: Dict[str, Any],
        abromics_samples: list = None,
        center_name: Optional[str] = None,
        submitter_name: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        Map an Abromics experiment to ENA study metadata.

        Parameters
        ----------
        abromics_experiment : dict
            Abromics experiment/analysis data
        abromics_samples : list, optional
            List of Abromics sample data to include sample-specific info in description
        center_name : str, optional
            Center name for ENA study (mandatory from Abromics UI)
        submitter_name : str, optional
            Optional submitter name stored in additional metadata

        Returns
        -------
        dict
            ENA-formatted study metadata
        """
        ena_study = {}

        # Study identification
        project = abromics_experiment.get('project', {})
        ena_study['study_title'] = project.get('name', 'Abromics Study')
        ena_study['study_alias'] = f"abromics_project_{project.get('id', 'unknown')}"

        # Study description - prioritize sample information over generic project description
        description_parts = []

        # Extract unique organism names from samples (more accurate than project description)
        if abromics_samples:
            organisms = set()
            for sample in abromics_samples:
                taxonomy = sample.get('taxonomy', {})
                if taxonomy:
                    scientific_name = taxonomy.get('scientific_name', '')
                    if scientific_name:
                        organisms.add(scientific_name)

            if organisms:
                org_list = sorted(list(organisms))
                if len(org_list) == 1:
                    description_parts.append(f"This project provides genomic data for {org_list[0]}.")
                else:
                    description_parts.append(f"This project provides genomic data for: {', '.join(org_list)}.")

        # Add project description only if no sample-specific info was found
        if not description_parts and project.get('description'):
            description_parts.append(project['description'])

        # Add workflow information
        workflow = abromics_experiment.get('workflow', {})
        if workflow:
            description_parts.append(f"Analysis performed using: {workflow.get('name', 'Unknown workflow')}")

        ena_study['study_description'] = ' '.join(description_parts) or 'Genomic analysis from Abromics platform'

        # Study type
        template = project.get('template', {})
        if template:
            template_type = template.get('type', 'genomic')
            ena_study['study_type'] = 'Other' if template_type == 'metagenomic' else 'Whole Genome Sequencing'

        # Center name (submitting institution)
        if center_name:
            ena_study['center_name'] = center_name

 
        additional_metadata = [
            {
                "tag": "submitted_from",
                "value": "Abromics Analysis Platform",
            }
        ]
        if submitter_name:
            additional_metadata.append(
                {
                    "tag": "submitter_name",
                    "value": submitter_name,
                }
            )
        ena_study["additional_metadata"] = additional_metadata

        return ena_study
    
    @staticmethod
    def map_files_to_raw_reads(abromics_sample: Dict[str, Any], 
                                 file_info: Dict[str, Any]) -> Dict[str, Any]:
        """
        Map Abromics file information to ENA raw read metadata.
        
        Parameters
        ----------
        abromics_sample : dict
            Abromics sample data
        file_info : dict
            File-specific information (may contain 'raw_file' for specific file data)
            
        Returns
        -------
        dict
            ENA-formatted raw read metadata
        """
        ena_raw_read = {}
        
        # Raw read identification
        sample_id = abromics_sample.get('original_sample_id', 'unknown')
        ena_raw_read['raw_read_title'] = f"Raw sequencing data for {sample_id}"
        ena_raw_read['raw_read_alias'] = f"abromics_rawread_{abromics_sample.get('id', 'unknown')}"
        
        # Get file information - prefer file_info.raw_file if available, otherwise use first from rawinputfiles
        raw_file = file_info.get('raw_file')
        if not raw_file:
            rawinputfiles = abromics_sample.get('rawinputfiles', [])
            if rawinputfiles and len(rawinputfiles) > 0:
                raw_file = rawinputfiles[0]

        instrument = 'unspecified'
        platform = 'UNSPECIFIED'
        if raw_file:
            sequencing_tech = raw_file.get('sequencing_technology', {})
            if sequencing_tech:
                instrument = sequencing_tech.get('instrument', 'unspecified')
                platform = sequencing_tech.get('platform') or 'UNSPECIFIED'

        ena_raw_read['instrument'] = {
            'platform': platform,
            'model': instrument,
        }

        # Library layout (MANDATORY) - determined by workflow template

        # Template #1 = Genomic paired-end FASTQ → paired
        # Template #2 = Genomic FASTA → single
        template = file_info.get('template', {}) or {}
        template_id = template.get('id')
        try:
            template_id = int(template_id) if template_id is not None else None
        except (ValueError, TypeError):
            template_id = None
    
        if template_id == 1:
            library_layout = 'paired'
        elif template_id == 2:
            library_layout = 'single'
        else:
            # Fallback when template id unknown
            library_layout = 'single'

        # Build library layout block; nominal_length is mandatory for paired-end (ENA schema)
        library_layout_block = {
            'library_layout': library_layout,
            'library_selection': 'RANDOM',
        }

        if library_layout == 'paired':
            # ENA requires nominal_length for paired-end.
            # TODO: extract from assembly (e.g. Picard CollectInsertSizeMetrics on shovill_alignment_bam)
            library_layout_block['nominal_length'] = 1

        ena_raw_read['library_layout'] = library_layout_block

        # Expose nominal_length as standalone field for UI visibility (paired-end only)
        if library_layout == 'paired':
            ena_raw_read['nominal_length'] = library_layout_block.get('nominal_length', 123)

        # Library source (MANDATORY) - always set
        ena_raw_read['library_source'] = 'GENOMIC'
        
        # Library strategy (MANDATORY) - always set
        ena_raw_read['library_strategy'] = 'WGS'
        
        # Design description (optional but recommended)
        workflow = file_info.get('workflow', {}) or {}
        if workflow:
            ena_raw_read['design_description'] = f"Sequencing for analysis: {workflow.get('name', '')}"
        else:
            ena_raw_read['design_description'] = "Sequencing for genomic analysis"
        
        return ena_raw_read
    
    @staticmethod
    def validate_required_fields(metadata: Dict[str, Any], object_type: str) -> tuple[bool, list]:
        """
        Validate that required ENA fields are present.
        
        Parameters
        ----------
        metadata : dict
            Metadata to validate
        object_type : str
            Type of object ('sample', 'study', or 'raw_read')
            
        Returns
        -------
        tuple
            (is_valid, list_of_missing_fields)
        """
        required_fields = {
            'sample': ['sample_title', 'sample_alias', 'sample_taxon'],
            'study': ['study_title', 'study_alias', 'study_description', 'center_name'],
            'raw_read': ['raw_read_title', 'raw_read_alias', 'instrument', 'library_layout', 
                         'library_source', 'library_strategy']
        }
        
        required = required_fields.get(object_type, [])
        missing = [field for field in required if field not in metadata or not metadata[field]]
        
        return len(missing) == 0, missing


# Create a global mapper instance
MAPPING_ABROMICS_ENA = AbromicsToENAMapper()


