# frozen_string_literal: true

require 'test_helper'

module ActionController
  module Serialization
    class JsonApi
      class LinkedTest < ActionDispatch::IntegrationTest
        class LinkedTestController < ActionController::Base
          def setup_post
            ActionController::Base.cache_store.clear
            @role1 = Role.new(id: 1, name: 'admin')
            @role2 = Role.new(id: 2, name: 'colab')
            @author = Author.new(id: 1, name: 'Steve K.')
            @author.posts = []
            @author.bio = nil
            @author.roles = [@role1, @role2]
            @role1.author = @author
            @role2.author = @author
            @author2 = Author.new(id: 2, name: 'Anonymous')
            @author2.posts = []
            @author2.bio = nil
            @author2.roles = []
            @post = Post.new(id: 1, title: 'New Post', body: 'Body')
            @first_comment = Comment.new(id: 1, body: 'ZOMG A COMMENT')
            @second_comment = Comment.new(id: 2, body: 'ZOMG ANOTHER COMMENT')
            @post.comments = [@first_comment, @second_comment]
            @post.author = @author
            @first_comment.post = @post
            @first_comment.author = @author2
            @second_comment.post = @post
            @second_comment.author = nil
            @post2 = Post.new(id: 2, title: 'Another Post', body: 'Body')
            @post2.author = @author
            @post2.comments = []
            @blog = Blog.new(id: 1, name: 'My Blog!!')
            @post.blog = @blog
            @post2.blog = @blog
          end

          def render_resource_without_include
            setup_post
            render json: @post
          end

          def render_resource_with_include
            setup_post
            render json: @post, adapter: :json_api, include: [:author]
          end

          def render_resource_with_include_of_custom_key_by_original
            setup_post
            render json: @post, adapter: :json_api, include: [:reviews], serializer: PostWithCustomKeysSerializer
          end

          def render_resource_with_nested_include
            setup_post
            render json: @post, adapter: :json_api, include: [comments: [:author]]
          end

          def render_resource_with_nested_has_many_include_wildcard
            setup_post
            render json: @post, adapter: :json_api, include: 'author.*'
          end

          def render_resource_with_missing_nested_has_many_include
            setup_post
            @post.author = @author2 # author2 has no roles.
            render json: @post, adapter: :json_api, include: [author: [:roles]]
          end

          def render_collection_with_missing_nested_has_many_include
            setup_post
            @post.author = @author2
            render json: [@post, @post2], adapter: :json_api, include: [author: [:roles]]
          end

          def render_collection_without_include
            setup_post
            render json: [@post], adapter: :json_api
          end

          def render_collection_with_include
            setup_post
            render json: [@post], adapter: :json_api, include: 'author,comments'
          end
        end

        setup do
          @routes = Rails.application.routes.draw do
            ActiveSupport::Deprecation.silence do
              match ':action', to: LinkedTestController, via: [:get, :post]
            end
          end
        end

        def test_render_resource_without_include
          get '/render_resource_without_include'
          response = JSON.parse(@response.body)
          refute response.key? 'included'
        end

        def test_render_resource_with_include
          get '/render_resource_with_include'
          response = JSON.parse(@response.body)
          assert response.key? 'included'
          assert_equal 1, response['included'].size
          assert_equal 'Steve K.', response['included'].first['attributes']['name']
        end

        def test_render_resource_with_nested_has_many_include
          get '/render_resource_with_nested_has_many_include_wildcard'
          response = JSON.parse(@response.body)
          expected_linked = [
            {
              'id' => '1',
              'type' => 'authors',
              'attributes' => {
                'name' => 'Steve K.'
              },
              'relationships' => {
                'posts' => { 'data' => [] },
                'roles' => { 'data' => [{ 'type' => 'roles', 'id' => '1' }, { 'type' => 'roles', 'id' => '2' }] },
                'bio' => { 'data' => nil }
              }
            }, {
              'id' => '1',
              'type' => 'roles',
              'attributes' => {
                'name' => 'admin',
                'description' => nil,
                'slug' => 'admin-1'
              },
              'relationships' => {
                'author' => { 'data' => { 'type' => 'authors', 'id' => '1' } }
              }
            }, {
              'id' => '2',
              'type' => 'roles',
              'attributes' => {
                'name' => 'colab',
                'description' => nil,
                'slug' => 'colab-2'
              },
              'relationships' => {
                'author' => { 'data' => { 'type' => 'authors', 'id' => '1' } }
              }
            }
          ]
          assert_equal expected_linked, response['included']
        end

        def test_render_resource_with_include_of_custom_key_by_original
          get '/render_resource_with_include_of_custom_key_by_original'
          response = JSON.parse(@response.body)
          assert response.key? 'included'

          relationships = response['data']['relationships']

          assert_includes relationships, 'reviews'
          assert_includes relationships, 'writer'
          assert_includes relationships, 'site'
        end

        def test_render_resource_with_nested_include
          get '/render_resource_with_nested_include'
          response = JSON.parse(@response.body)
          assert response.key? 'included'
          assert_equal 3, response['included'].size
        end

        def test_render_collection_without_include
          get '/render_collection_without_include'
          response = JSON.parse(@response.body)
          refute response.key? 'included'
        end

        def test_render_collection_with_include
          get '/render_collection_with_include'
          response = JSON.parse(@response.body)
          assert response.key? 'included'
        end

        def test_render_resource_with_nested_attributes_even_when_missing_associations
          get '/render_resource_with_missing_nested_has_many_include'
          response = JSON.parse(@response.body)
          assert response.key? 'included'
          refute include_type?(response['included'], 'roles')
        end

        def test_render_collection_with_missing_nested_has_many_include
          get '/render_collection_with_missing_nested_has_many_include'
          response = JSON.parse(@response.body)
          assert response.key? 'included'
          assert include_type?(response['included'], 'roles')
        end

        def include_type?(collection, value)
          collection.detect { |i| i['type'] == value }
        end
      end
    end
  end
end
