# frozen_string_literal: true

require 'spec_helper'

RSpec.describe CKEditor5::Rails::Hooks::Form do
  describe CKEditor5::Rails::Hooks::Form::EditorInputBuilder do
    let(:post) { Post.new(content: 'Initial content') }
    let(:builder) { described_class.new(:post, post, template) }
    let(:template) do
      Class.new(ActionView::Base) do
        def content_security_policy_nonce
          'test-nonce'
        end
      end.new(ActionView::LookupContext.new([]), {}, nil)
    end

    before do
      template.ckeditor5_assets(version: '34.1.0', automatic_upgrades: false, cdn: :jsdelivr)
    end

    describe '#build_editor' do
      subject(:rendered_editor) { builder.build_editor(:content) }

      it 'renders ckeditor element' do
        bundle_json = {
          scripts: [
            {
              import_name: 'ckeditor5',
              url: 'https://cdn.jsdelivr.net/npm/ckeditor5@34.1.0/dist/browser/ckeditor5.js',
              translation: false
            }
          ],
          stylesheets: [
            'https://cdn.jsdelivr.net/npm/ckeditor5@34.1.0/dist/browser/ckeditor5.css'
          ]
        }.to_json

        attrs = {
          name: 'post[content]',
          id: 'post_content',
          type: 'ClassicEditor',
          bundle: bundle_json,
          watchdog: 'true'
        }

        expect(rendered_editor).to have_tag('ckeditor-component', with: attrs)
      end

      context 'with custom attributes' do
        subject(:rendered_editor) do
          builder.build_editor(:content, class: 'custom-class', id: 'custom-id', name: 'custom-name')
        end

        it 'respects custom HTML attributes' do
          expect(rendered_editor).to have_tag('ckeditor-component', with: {
                                                class: 'custom-class',
                                                id: 'custom-id',
                                                name: 'custom-name'
                                              })
        end
      end

      context 'with as parameter' do
        subject(:rendered_editor) do
          builder.build_editor(:content, as: 'custom_field')
        end

        it 'uses custom field name' do
          expect(rendered_editor).to have_tag('ckeditor-component', with: {
                                                name: 'post[custom_field]'
                                              })
        end
      end

      context 'without object_name' do
        let(:builder) { described_class.new('', post, template) }

        it 'uses method name as field name' do
          expect(rendered_editor).to have_tag('ckeditor-component', with: {
                                                name: 'content'
                                              })
        end

        context 'with as parameter' do
          subject(:rendered_editor) do
            builder.build_editor(:content, as: 'custom_field')
          end

          it 'uses as parameter as field name' do
            expect(rendered_editor).to have_tag('ckeditor-component', with: {
                                                  name: 'custom_field'
                                                })
          end
        end
      end

      context 'with initial data handling' do
        before do
          allow(template).to receive(:ckeditor5_editor)
        end

        context 'when object responds to the method' do
          it 'passes object method value as initial_data' do
            builder.build_editor(:content)

            expect(template).to have_received(:ckeditor5_editor)
              .with(hash_including(initial_data: 'Initial content'))
          end
        end

        context 'when object does not respond to the method' do
          let(:post) { double('Post') }

          it 'passes options initial_data value' do
            builder.build_editor(:content, initial_data: 'Provided content')

            expect(template).to have_received(:ckeditor5_editor)
              .with(hash_including(initial_data: 'Provided content'))
          end
        end
      end

      context 'with validation classes handling' do
        before do
          allow(template).to receive(:ckeditor5_editor)
        end

        context 'when object has errors on the field' do
          let(:post) do
            instance_double('Post', errors: { content: ['is invalid'] })
          end

          it 'adds is-invalid class' do
            builder.build_editor(:content, class: 'custom-class')

            expect(template).to have_received(:ckeditor5_editor)
              .with(hash_including(class: 'custom-class is-invalid'))
          end

          it 'adds is-invalid class when no initial class exists' do
            builder.build_editor(:content)

            expect(template).to have_received(:ckeditor5_editor)
              .with(hash_including(class: 'is-invalid'))
          end
        end

        context 'when object has no errors' do
          let(:post) do
            instance_double('Post', errors: {})
          end

          it 'keeps original class unchanged' do
            builder.build_editor(:content, class: 'custom-class')

            expect(template).to have_received(:ckeditor5_editor)
              .with(hash_including(class: 'custom-class'))
          end
        end

        context 'when object does not respond to errors' do
          let(:post) { double('Post') }

          it 'keeps original class unchanged' do
            builder.build_editor(:content, class: 'custom-class')

            expect(template).to have_received(:ckeditor5_editor)
              .with(hash_including(class: 'custom-class'))
          end
        end
      end
    end
  end

  describe CKEditor5::Rails::Hooks::Form::FormBuilderExtension do
    let(:template) { instance_double('ActionView::Base') }
    let(:object) { double('Post') }
    let(:builder) do
      Class.new do
        include CKEditor5::Rails::Hooks::Form::FormBuilderExtension
        attr_reader :object_name, :object, :template

        def initialize(object_name, object, template)
          @object_name = object_name
          @object = object
          @template = template
        end
      end.new('post', object, template)
    end

    describe '#ckeditor5' do
      let(:input_builder) { instance_double(CKEditor5::Rails::Hooks::Form::EditorInputBuilder) }

      before do
        allow(CKEditor5::Rails::Hooks::Form::EditorInputBuilder)
          .to receive(:new)
          .with('post', object, template)
          .and_return(input_builder)
        allow(input_builder).to receive(:build_editor)
      end

      it 'creates EditorInputBuilder with correct parameters' do
        builder.ckeditor5(:content, class: 'custom-class')

        expect(CKEditor5::Rails::Hooks::Form::EditorInputBuilder)
          .to have_received(:new)
          .with('post', object, template)
      end

      it 'calls build_editor with correct parameters' do
        options = { class: 'custom-class' }
        builder.ckeditor5(:content, options)

        expect(input_builder)
          .to have_received(:build_editor)
          .with(:content, options)
      end
    end
  end
end
