#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++

require 'spec_helper'

describe ::API::V3::Grids::Schemas::GridSchemaRepresenter do
  include API::V3::Utilities::PathHelper

  let(:current_user) { FactoryBot.build_stubbed(:user) }

  let(:self_link) { '/a/self/link' }
  let(:embedded) { true }
  let(:new_record) { true }
  let(:allowed_pages) { %w(/some/path /some/other/path) }
  let(:allowed_widgets) do
    [
      OpenStruct.new(
        identifier: 'first_widget'
      ),
      OpenStruct.new(
        identifier: 'second_widget'
      )
    ]
  end
  let(:contract) do
    contract = double('contract')

    allow(contract)
      .to receive(:writable?) do |attribute|
      writable = %w(row_count column_count widgets)

      if new_record
        writable << 'page'
      end

      writable.include?(attribute.to_s)
    end

    allow(contract)
      .to receive(:assignable_values)
      .with(:page, current_user)
      .and_return(allowed_pages)

    allow(contract)
      .to receive(:assignable_values)
      .with(:widgets, current_user)
      .and_return(allowed_widgets)

    contract
  end
  let(:representer) do
    described_class.create(contract,
                           self_link,
                           form_embedded: embedded,
                           current_user: current_user)
  end

  context 'generation' do
    subject(:generated) { representer.to_json }

    describe '_type' do
      it 'is indicated as Schema' do
        is_expected.to be_json_eql('Schema'.to_json).at_path('_type')
      end
    end

    describe 'id' do
      let(:path) { 'id' }

      it_behaves_like 'has basic schema properties' do
        let(:type) { 'Integer' }
        let(:name) { I18n.t('attributes.id') }
        let(:required) { true }
        let(:writable) { false }
      end

      it_behaves_like 'has no visibility property'
    end

    describe 'rowCount' do
      let(:path) { 'rowCount' }

      it_behaves_like 'has basic schema properties' do
        let(:type) { 'Integer' }
        let(:name) { Grids::Grid.human_attribute_name(:row_count) }
        let(:required) { true }
        let(:writable) { true }
      end

      it_behaves_like 'has no visibility property'
    end

    describe 'columnCount' do
      let(:path) { 'columnCount' }

      it_behaves_like 'has basic schema properties' do
        let(:type) { 'Integer' }
        let(:name) { Grids::Grid.human_attribute_name(:column_count) }
        let(:required) { true }
        let(:writable) { true }
      end

      it_behaves_like 'has no visibility property'
    end

    describe 'createdAt' do
      let(:path) { 'createdAt' }

      it_behaves_like 'has basic schema properties' do
        let(:type) { 'DateTime' }
        let(:name) { Grids::Grid.human_attribute_name('created_at') }
        let(:required) { true }
        let(:writable) { false }
      end

      it_behaves_like 'has no visibility property'
    end

    describe 'updatedAt' do
      let(:path) { 'updatedAt' }

      it_behaves_like 'has basic schema properties' do
        let(:type) { 'DateTime' }
        let(:name) { Grids::Grid.human_attribute_name('updated_at') }
        let(:required) { true }
        let(:writable) { false }
      end

      it_behaves_like 'has no visibility property'
    end

    describe 'widgets' do
      let(:path) { 'widgets' }

      it_behaves_like 'has basic schema properties' do
        let(:type) { '[]GridWidget' }
        let(:name) { Grids::Grid.human_attribute_name('widgets') }
        let(:required) { true }
        let(:writable) { true }
      end

      context 'when embedding' do
        let(:embedded) { true }

        it 'contains no link to the allowed values' do
          is_expected.not_to have_json_path("#{path}/_links/allowedValues")
        end

        it 'embeds the allowed values' do
          allowed_widgets.each_with_index do |widget, index|
            href_path = "#{path}/_embedded/allowedValues/#{index}/identifier"
            is_expected.to be_json_eql(widget[:identifier].to_json).at_path(href_path)
          end
        end
      end

      context 'when not embedding' do
        let(:embedded) { false }

        it_behaves_like 'does not link to allowed values'
      end
    end

    describe 'page' do
      let(:path) { 'page' }

      context 'when having a new record' do
        it_behaves_like 'has basic schema properties' do
          let(:type) { 'Href' }
          let(:name) { Grids::Grid.human_attribute_name('page') }
          let(:required) { true }
          let(:writable) { true }
        end

        context 'when embedding' do
          let(:embedded) { true }

          it_behaves_like 'links to allowed values directly' do
            let(:hrefs) { allowed_pages }
          end

          it 'does not embed' do
            expect(generated)
              .not_to have_json_path('page/embedded')
          end
        end

        context 'when not embedding' do
          let(:embedded) { false }

          it_behaves_like 'does not link to allowed values'

          it 'does not embed' do
            expect(generated)
              .not_to have_json_path('page/embedded')
          end
        end
      end

      context 'when not having a new record' do
        let(:new_record) { false }
        let(:allowed_pages) { nil }

        it_behaves_like 'has basic schema properties' do
          let(:type) { 'Href' }
          let(:name) { Grids::Grid.human_attribute_name('page') }
          let(:required) { true }
          let(:writable) { false }
        end

        context 'when embedding' do
          let(:embedded) { true }

          it_behaves_like 'does not link to allowed values'

          it 'does not embed' do
            expect(generated)
              .not_to have_json_path('page/embedded')
          end
        end

        context 'when not embedding' do
          let(:embedded) { false }

          it_behaves_like 'does not link to allowed values'

          it 'does not embed' do
            expect(generated)
              .not_to have_json_path('page/embedded')
          end
        end
      end
    end

    context '_links' do
      describe 'self link' do
        it_behaves_like 'has an untitled link' do
          let(:link) { 'self' }
          let(:href) { self_link }
        end

        context 'embedded in a form' do
          let(:self_link) { nil }

          it_behaves_like 'has no link' do
            let(:link) { 'self' }
          end
        end
      end
    end
  end
end
