require 'test_helper'

class SaveTest < Minitest::Spec
  module Model
    Song  = Struct.new(:title, :composer)
    Album = Struct.new(:name, :songs, :artist)
    Artist = Struct.new(:name)
  end


  module Twin
    class Album < Disposable::Twin
      feature Setup
      feature Sync
      feature Save

      property :name

      collection :songs do
        property :title

        property :composer do
          property :name
        end
      end

      property :artist do
        property :name
      end
    end
  end


  let (:song) { Model::Song.new().extend(Disposable::Saveable) }
  let (:composer) { Model::Artist.new(nil).extend(Disposable::Saveable) }
  let (:song_with_composer) { Model::Song.new(nil, composer).extend(Disposable::Saveable) }
  let (:artist) { Model::Artist.new(nil).extend(Disposable::Saveable) }


  let (:album) { Model::Album.new(nil, [song, song_with_composer], artist).extend(Disposable::Saveable) }

  let (:twin) { Twin::Album.new(album) }

  # with populated model.
  it do
    fill_out!(twin)

    twin.save

    # sync happened.
    expect(album.name).must_equal "Live And Dangerous"
    expect(album.songs[0]).must_be_instance_of Model::Song
    expect(album.songs[1]).must_be_instance_of Model::Song
    expect(album.songs[0].title).must_equal "Southbound"
    expect(album.songs[1].title).must_equal "The Boys Are Back In Town"
    expect(album.songs[1].composer).must_be_instance_of Model::Artist
    expect(album.songs[1].composer.name).must_equal "Lynott"
    expect(album.artist).must_be_instance_of Model::Artist
    expect(album.artist.name).must_equal "Thin Lizzy"

    # saved?
    expect(album.saved?).must_equal true
    expect(album.songs[0].saved?).must_equal true
    expect(album.songs[1].saved?).must_equal true
    expect(album.songs[1].composer.saved?).must_equal true
    expect(album.artist.saved?).must_equal true
  end

  #save returns result.
  it { expect(twin.save).must_equal true }
  it do
    album.instance_eval { def save; false; end }
    expect(twin.save).must_equal false
  end

  # with save{}.
  it do
    twin = Twin::Album.new(album)

    # this usually happens in Contract::Validate or in from_* in a representer
    fill_out!(twin)

    nested_hash = nil
    twin.save do |hash|
      nested_hash = hash
    end

    expect(nested_hash).must_equal({"name"=>"Live And Dangerous", "songs"=>[{"title"=>"Southbound", "composer"=>nil}, {"title"=>"The Boys Are Back In Town", "composer"=>{"name"=>"Lynott"}}], "artist"=>{"name"=>"Thin Lizzy"}})

    # nothing written to model.
    expect(album.name).must_be_nil
    expect(album.songs[0].title).must_be_nil
    expect(album.songs[1].title).must_be_nil
    expect(album.songs[1].composer.name).must_be_nil
    expect(album.artist.name).must_be_nil

    # nothing saved.
    # saved?
    expect(album.saved?).must_be_nil
    expect(album.songs[0].saved?).must_be_nil
    expect(album.songs[1].saved?).must_be_nil
    expect(album.songs[1].composer.saved?).must_be_nil
    expect(album.artist.saved?).must_be_nil
  end


  # save: false
  module Twin
    class AlbumWithSaveFalse < Disposable::Twin
      feature Setup
      feature Sync
      feature Save

      property :name

      collection :songs, save: false do
        property :title

        property :composer do
          property :name
        end
      end

      property :artist do
        property :name
      end
    end
  end

  # with save: false.
  it do
    twin = Twin::AlbumWithSaveFalse.new(album)

    fill_out!(twin)

    twin.save

    # sync happened.
    expect(album.name).must_equal "Live And Dangerous"
    expect(album.songs[0]).must_be_instance_of Model::Song
    expect(album.songs[1]).must_be_instance_of Model::Song
    expect(album.songs[0].title).must_equal "Southbound"
    expect(album.songs[1].title).must_equal "The Boys Are Back In Town"
    expect(album.songs[1].composer).must_be_instance_of Model::Artist
    expect(album.songs[1].composer.name).must_equal "Lynott"
    expect(album.artist).must_be_instance_of Model::Artist
    expect(album.artist.name).must_equal "Thin Lizzy"

    # saved?
    expect(album.saved?).must_equal true
    expect(album.songs[0].saved?).must_be_nil
    expect(album.songs[1].saved?).must_be_nil
    expect(album.songs[1].composer.saved?).must_be_nil # doesn't get saved.
    expect(album.artist.saved?).must_equal true
  end

  def fill_out!(twin)
    twin.name = "Live And Dangerous"
    twin.songs[0].title = "Southbound"
    twin.songs[1].title = "The Boys Are Back In Town"
    twin.songs[1].composer.name = "Lynott"
    twin.artist.name = "Thin Lizzy"
  end
end


# TODO: with block

# class SaveWithDynamicOptionsTest < Minitest::Spec
#   Song = Struct.new(:id, :title, :length) do
#     include Disposable::Saveable
#   end

#   class SongForm < Reform::Form
#     property :title#, save: false
#     property :length, virtual: true
#   end

#   let (:song) { Song.new }
#   let (:form) { SongForm.new(song) }

#   # we have access to original input value and outside parameters.
#   it "xxx" do
#     form.validate("title" => "A Poor Man's Memory", "length" => 10)
#     length_seconds = 120
#     form.save(length: lambda { |value, options| form.model.id = "#{value}: #{length_seconds}" })

#     song.title).must_equal "A Poor Man's Memory"
#     song.length).must_be_nil
#     song.id).must_equal "10: 120"
#   end
# end
