import argschema
import pathlib
from argschema.schemas import DefaultSchema
from marshmallow import post_load
from marshmallow.validate import OneOf


class BaseMetadataWriterInputSchema(argschema.ArgSchema):

    behavior_nwb_dir = argschema.fields.InputDir(
        required=True,
        default=None,
        allow_none=True,
        description=(
            "The directory where behavior_nwb sessions are to be found."
        ),
    )
    behavior_nwb_prefix = argschema.fields.Str(
        required=False,
        default="behavior_session",
        description=(
            "Behavior session NWB files will be looked for in the form "
            "{behavior_nwb_dir}/"
            "{behavior_nwb_prefix}_{behavior_session_id}.nwb"
        ),
    )

    output_dir = argschema.fields.OutputDir(
        required=True,
        description=(
            "Directory to output metadata tables."
        ),
    )

    clobber = argschema.fields.Boolean(
        default=False,
        description=(
            "If false, throw an error if output files already exist."
        ),
    )

    on_missing_file = argschema.fields.Str(
        default="error",
        required=False,
        validation=OneOf(("error", "warn", "skip")),
        description=(
            "What to do if an input datafile is missing. "
            "If 'error', raise an exception. "
            "If 'warn', assign a dummy ID and issue a warning. "
            "If 'skip', do not list in metadata and issue a "
            "warning (note, any sessions thus skipped will still "
            "show up in aggregate metadata; there just will "
            "be no line for those sessions in tables that list "
            "data files for release, like sessions.csv)."
        ),
    )


class BehaviorOphysMetadataInputSchema(BaseMetadataWriterInputSchema):

    data_release_date = argschema.fields.List(
        argschema.fields.String,
        required=True,
        cli_as_single_argument=False,
        description=(
            "Sub-select the set sessions/experiments to release based on a "
            "release date. "
            "(e.g. --data_release_date '2021-03-25' '2021-08-12')"
        ),
    )
    ophys_nwb_dir = argschema.fields.InputDir(
        required=True,
        allow_none=True,
        default=None,
        description=(
            "The directory where ophys experiments are to be found."
        ),
    )
    ophys_nwb_prefix = argschema.fields.Str(
        required=False,
        default="behavior_ophys_experiment",
        description=(
            "Ophys experiment NWB files will be looked for "
            "in the form "
            "{ophys_nwb_dir}/{ophys_nwb_prefix}_{ophys_experiment_id}.nwb"
        ),
    )

    @post_load
    def validate_paths(self, data, **kwargs):
        fname_lookup = {
            'behavior_session_table': 'behavior_session_table.csv',
            'ophys_session_table': 'ophys_session_table.csv',
            'ophys_experiment_table': 'ophys_experiment_table.csv',
            'ophys_cells_table': 'ophys_cells_table.csv'
        }

        out_dir = pathlib.Path(data["output_dir"])
        msg = ""
        for fname_k in fname_lookup.keys():
            full_path = out_dir / fname_lookup[fname_k]
            if full_path.exists() and not data["clobber"]:
                msg += f"{full_path.resolve().absolute()}\n"
            data[fname_k] = str(full_path.resolve().absolute())

        if len(msg) > 0:
            raise RuntimeError(
                "The following files already exist\n"
                f"{msg}"
                "Run with clobber=True if you want to overwrite"
            )
        return data


class PipelineMetadataSchema(DefaultSchema):

    name = argschema.fields.Str(
        required=True,
        allow_none=False,
        description=("Name of the pipeline component (e.g. 'AllenSDK')"),
    )

    version = argschema.fields.Str(
        required=True,
        allow_none=False,
        description=("Semantic version of the pipeline component"),
    )

    comment = argschema.fields.Str(
        required=False,
        default="",
        description=("Optional comment about this piece of software"),
    )


class DataReleaseToolsInputSchema(argschema.ArgSchema):
    """
    This schema will be used as the output schema for
    data_release.metadata_writer modules. It is actually
    a subset of the input schema for the
    informatics_data_release_tools (the output of the metadata
    writers is meant to be the input of the data_release_tool)
    """

    metadata_files = argschema.fields.List(
        argschema.fields.InputFile,
        description=(
            "Paths to the metadata '.csv' files written by this modules."
        ),
    )

    data_pipeline_metadata = argschema.fields.Nested(
        PipelineMetadataSchema,
        many=True,
        description=(
            "Metadata about the pipeline used to create this data release."
        ),
    )

    project_name = argschema.fields.Str(
        required=True,
        allow_none=False,
        description=(
            "The project name to be passed along to the data_release_tool "
            "when uploading this dataset."
        ),
    )
