# This file was generated by tools/generate_dataset_fields.py
import dataclasses
from typing import Any, Dict, Optional, Union
import typing

from pyscicat.model import DatasetType, DerivedDataset, RawDataset

_NAME_IN_DERIVED = {
    "created_by": "createdBy",
    "updated_by": "updatedBy",
    "updated_at": "updatedAt",
    "created_at": "createdAt",
    "owner_group": "ownerGroup",
    "access_groups": "accessGroups",
    "pid": "pid",
    "classification": "classification",
    "contact_email": "contactEmail",
    "creation_time": "creationTime",
    "dataset_name": "datasetName",
    "description": "description",
    "history": "history",
    "instrument_id": "instrumentId",
    "is_published": "isPublished",
    "keywords": "keywords",
    "license": "license",
    "number_of_files": "numberOfFiles",
    "number_of_files_archived": "numberOfFilesArchived",
    "orcid_of_owner": "orcidOfOwner",
    "packed_size": "packedSize",
    "owner": "owner",
    "owner_email": "ownerEmail",
    "shared_with": "sharedWith",
    "size": "size",
    "source_folder": "sourceFolder",
    "source_folder_host": "sourceFolderHost",
    "techniques": "techniques",
    "dataset_type": "type",
    "validation_status": "validationStatus",
    "version": "version",
    "investigator": "investigator",
    "input_datasets": "inputDatasets",
    "used_software": "usedSoftware",
    "job_parameters": "jobParameters",
    "job_log_data": "jobLogData",
    "scientific_metadata": "scientificMetadata",
}
_NAME_IN_RAW = {
    "created_by": "createdBy",
    "updated_by": "updatedBy",
    "updated_at": "updatedAt",
    "created_at": "createdAt",
    "owner_group": "ownerGroup",
    "access_groups": "accessGroups",
    "pid": "pid",
    "classification": "classification",
    "contact_email": "contactEmail",
    "creation_time": "creationTime",
    "dataset_name": "datasetName",
    "description": "description",
    "history": "history",
    "instrument_id": "instrumentId",
    "is_published": "isPublished",
    "keywords": "keywords",
    "license": "license",
    "number_of_files": "numberOfFiles",
    "number_of_files_archived": "numberOfFilesArchived",
    "orcid_of_owner": "orcidOfOwner",
    "packed_size": "packedSize",
    "owner": "owner",
    "owner_email": "ownerEmail",
    "shared_with": "sharedWith",
    "size": "size",
    "source_folder": "sourceFolder",
    "source_folder_host": "sourceFolderHost",
    "techniques": "techniques",
    "dataset_type": "type",
    "validation_status": "validationStatus",
    "version": "version",
    "investigator": "principalInvestigator",
    "creation_location": "creationLocation",
    "data_format": "dataFormat",
    "end_time": "endTime",
    "sample_id": "sampleId",
    "proposal_id": "proposalId",
    "scientific_metadata": "scientificMetadata",
}
_OMITTED_FIELDS = (
    "number_of_files",
    "number_of_files_archived",
    "packed_size",
    "pid",
    "size",
    "scientific_metadata",
)


@dataclasses.dataclass
class DatasetFields:
    access_groups: typing.List[str]
    contact_email: str
    creation_time: str
    dataset_type: DatasetType
    owner: str
    owner_group: str
    source_folder: str
    classification: Optional[str] = None
    created_at: Optional[str] = None
    created_by: Optional[str] = None
    creation_location: Optional[str] = None
    data_format: Optional[str] = None
    dataset_name: Optional[str] = None
    description: Optional[str] = None
    end_time: Optional[str] = None
    history: Optional[typing.List[dict]] = None
    input_datasets: Optional[typing.List[str]] = None
    instrument_id: Optional[str] = None
    investigator: Optional[str] = None
    is_published: Optional[bool] = None
    job_log_data: Optional[str] = None
    job_parameters: Optional[dict] = None
    keywords: Optional[typing.List[str]] = None
    license: Optional[str] = None
    orcid_of_owner: Optional[str] = None
    owner_email: Optional[str] = None
    proposal_id: Optional[str] = None
    sample_id: Optional[str] = None
    shared_with: Optional[typing.List[str]] = None
    source_folder_host: Optional[str] = None
    techniques: Optional[typing.List[dict]] = None
    updated_at: Optional[str] = None
    updated_by: Optional[str] = None
    used_software: Optional[typing.List[str]] = None
    validation_status: Optional[str] = None
    version: Optional[str] = None

    def _map_fields(self, name_mapping, type_name: str) -> Dict[str, Any]:
        mapped = {}
        for name, val in filter(
            lambda t: t[1] is not None, dataclasses.asdict(self).items()
        ):
            try:
                mapped[name_mapping[name]] = val
            except KeyError:
                raise TypeError(
                    f"Field {name} is not allowed" f" in {type_name} datasets"
                ) from None
        return mapped

    def _map_to_model(
        self,
        number_of_files: Optional[int],
        number_of_files_archived: Optional[int],
        packed_size: Optional[int],
        pid: Optional[str],
        size: Optional[int],
        scientific_metadata: Optional[typing.Dict],
    ) -> Union[DerivedDataset, RawDataset]:
        if self.dataset_type == DatasetType.derived:
            name_mapping = _NAME_IN_DERIVED
            type_name = "derived"
            model = DerivedDataset
        else:
            name_mapping = _NAME_IN_RAW
            type_name = "raw"
            model = RawDataset
        mapped = self._map_fields(name_mapping, type_name)
        mapped[name_mapping["number_of_files"]] = number_of_files
        mapped[name_mapping["number_of_files_archived"]] = number_of_files_archived
        mapped[name_mapping["packed_size"]] = packed_size
        mapped[name_mapping["pid"]] = pid
        mapped[name_mapping["size"]] = size
        mapped[name_mapping["scientific_metadata"]] = scientific_metadata
        return model(**mapped)

    @staticmethod
    def _find_field_name_for_model_name(s, mapping):
        for scitacean_name, model_name in mapping.items():
            if model_name == s:
                return scitacean_name
        raise ValueError(f"{s} is not a valid field name")

    @staticmethod
    def _map_model_to_field_dict(model):
        d = {
            DatasetFields._find_field_name_for_model_name(
                name,
                _NAME_IN_DERIVED if model.type == DatasetType.derived else _NAME_IN_RAW,
            ): val
            for name, val in model.dict(exclude_none=True).items()
        }
        for name in _OMITTED_FIELDS:
            d.pop(name, None)
        return d
