﻿// Copyright 2021 Google LLC
//
// 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
//
//     https://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.

using NHibernate.Mapping.ByCode;
using System;
using System.Collections.Generic;

namespace Google.Cloud.Spanner.NHibernate.Samples.SampleModel
{
    public class Singer : AbstractVersionedEntity
    {
        public virtual string FirstName { get; set; }
        public virtual string LastName { get; set; }

        /// <summary>
        /// FullName is a calculated column. It will always automatically be generated by Cloud Spanner and
        /// may not be included in any INSERT/UPDATE statement. NHibernate will issue an extra SELECT
        /// statement when a Singer instance is updated to get the most recent value of the column.
        /// </summary>
        public virtual string FullName { get; set; }

        /// <summary>
        /// DATE columns are best mapped to <see cref="SpannerDate"/>.
        /// This makes it easier to distinguish them from TIMESTAMP columns, which are mapped to <see cref="DateTime"/>.
        /// </summary>
        public virtual SpannerDate BirthDate { get; set; }
        public virtual byte[] Picture { get; set; }

        public virtual IList<Album> Albums { get; set; }
        public virtual IList<Concert> Concerts { get; set; }
        public virtual IList<BandMembership> BandMemberships { get; set; }
    }

    public class SingerMapping : VersionedEntityMapping<Singer>
    {
        public SingerMapping()
        {
            Table("Singers");
            Property(x => x.FirstName, m => m.Length(200));
            Property(x => x.LastName, m =>
            {
                m.Length(200);
                m.NotNullable(true);
            });
            Property(x => x.FullName, m =>
            {
                m.Length(400);
                m.NotNullable(true);
                m.Generated(PropertyGeneration.Always);
                m.Index("Idx_Singers_FullName");
            });
            Property(x => x.BirthDate);
            Property(x => x.Picture);
            Bag(x => x.Albums, c =>
            {
                // Always mark the collection end of a one-to-many relationship as Inverse(true).
                // This will ensure that NHibernate will always first insert the parent record (the Singer)
                // before the child record (the Album). This reduces the number of DML statements that are
                // needed for a one-to-many relationship significantly.
                c.Inverse(true);
                c.Key(k => k.Column("SingerId"));
            }, r => r.OneToMany());
            Bag(x => x.Concerts, c =>
            {
                c.Inverse(true);
                c.Key(k => k.Column("SingerId"));
            }, r => r.OneToMany());
            Bag(x => x.BandMemberships,
                collectionMapping =>
                {
                    // Always set the one-to-many side of a collection mapping to Inverse(true).
                    // This prevents NHibernate from trying to delete and re-insert all elements in a collection when
                    // the collection is modified. This requires that the many-to-one side of the mapping always sets
                    // a value for the parent entity. That is, BandMember.Singer must always be set when a new
                    // BandMembership is created.
                    collectionMapping.Inverse(true);
                    collectionMapping.Key(key => key.Column("SingerId"));
                },
                r => r.OneToMany());
        }
    }
}
