#!/usr/bin/env python

# This work was created by participants in the DataONE project, and is
# jointly copyrighted by participating institutions in DataONE. For
# more information on DataONE, see our web site at http://dataone.org.
#
#   Copyright 2009-2019 DataONE
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Generate random SystemMetadata."""

import datetime
import os
import random

import d1_common.checksum
import d1_common.system_metadata

import d1_test.instance_generator.access_policy
import d1_test.instance_generator.checksum
import d1_test.instance_generator.date_time
import d1_test.instance_generator.format_id
import d1_test.instance_generator.identifier
import d1_test.instance_generator.media_type
import d1_test.instance_generator.random_data
import d1_test.instance_generator.replica
import d1_test.instance_generator.replication_policy


def generate_random(client, option_dict=None, normalize=True):
    """Generate a random System Metadata object.

    ``options`` is a set of key-value pairs that allow the caller to prevent
    specific sections of the System Metadata from being randomly generated by
    providing previously created objects of the appropriate types.

    E.g., providing an Identifier object in options['identifier'] causes that
    object to be used as the PID instead of a randomly generated Identifier.

    """
    option_dict = option_dict or {}

    sysmeta_pyxb = client.pyxb_binding.systemMetadata()
    sysmeta_pyxb.serialVersion = random.randint(1, 100)
    sysmeta_pyxb.identifier = lazy_get(
        option_dict, "identifier", d1_test.instance_generator.identifier.generate_pid
    )
    sysmeta_pyxb.formatId = lazy_get(
        option_dict, "formatId", d1_test.instance_generator.format_id.generate
    )
    sysmeta_pyxb.size = lazy_get(option_dict, "size", random.randint, 1, 1024 ** 4)
    sysmeta_pyxb.checksum = lazy_get(
        option_dict, "checksum", d1_test.instance_generator.checksum.generate
    )
    sysmeta_pyxb.submitter = lazy_get(
        option_dict, "submitter", d1_test.instance_generator.random_data.random_subj
    )
    sysmeta_pyxb.rightsHolder = lazy_get(
        option_dict, "rightsHolder", d1_test.instance_generator.random_data.random_subj
    )
    sysmeta_pyxb.accessPolicy = lazy_get(
        option_dict,
        "accessPolicy",
        d1_test.instance_generator.access_policy.generate,
        min_rules=0,
    )
    sysmeta_pyxb.replicationPolicy = lazy_get(
        option_dict,
        "replicationPolicy",
        d1_test.instance_generator.replication_policy.generate,
    )

    # obsoletes
    # obsoletedBy
    # archived

    sysmeta_pyxb.dateUploaded = lazy_get(
        option_dict,
        "dateUploaded",
        d1_test.instance_generator.date_time.reproducible_datetime,
        sysmeta_pyxb.identifier.value(),
    )
    # Set dateSysMetadataModified to a fixed time after dateUploaded
    sysmeta_pyxb.dateSysMetadataModified = option_dict.get(
        "dateSysMetadataModified",
        sysmeta_pyxb.dateUploaded + datetime.timedelta(days=10),
    )
    sysmeta_pyxb.originMemberNode = lazy_get(
        option_dict,
        "originMemberNode",
        d1_test.instance_generator.random_data.random_mn,
    )
    sysmeta_pyxb.authoritativeMemberNode = lazy_get(
        option_dict,
        "authoritativeMemberNode",
        d1_test.instance_generator.random_data.random_mn,
    )
    sysmeta_pyxb.replica = lazy_get(
        option_dict, "replica", d1_test.instance_generator.replica.generate
    )
    sysmeta_pyxb.seriesId = lazy_get(
        option_dict,
        "seriesId",
        d1_test.instance_generator.identifier.generate_sid,
        probability=0.5,
    )
    sysmeta_pyxb.mediaType = lazy_get(
        option_dict, "mediaType", d1_test.instance_generator.media_type.generate
    )
    sysmeta_pyxb.fileName = lazy_get(
        option_dict,
        "fileName",
        "file_{}".format,
        d1_test.instance_generator.random_data.random_lower_ascii(
            min_len=12, max_len=12
        ),
    )
    if normalize:
        d1_common.system_metadata.normalize_in_place(sysmeta_pyxb)
    return sysmeta_pyxb


def lazy_get(d, key, func, *args, **kwargs):
    """dict.get(k, f()) will evaluate b() even if s exists. This is a workaround.
    """
    try:
        return d[key]
    except KeyError:
        return func(*args, **kwargs)


def generate_from_file(client, f, option_dict=None):
    option_dict = option_dict or {}
    option_dict["checksum"] = d1_common.checksum.create_checksum_object_from_stream(f)
    f.seek(0, os.SEEK_END)
    option_dict["size"] = f.tell()
    return generate_random(client, option_dict)


def generate_from_file_path(client, path, option_dict=None):
    with open(path, "rb") as f:
        return generate_from_file(client, f, option_dict)
