# frozen_string_literal: true

# This example is based on this question:
# https://stackoverflow.com/questions/51955217/import-data-from-csv-into-two-tables-in-rails

require 'spec_helper'
require 'csv'

# Examples
# ========

RSpec.describe 'Parent', type: :model do
  before(:all) do
    # Set up Models
    # =============
    unload_class('Parent')
    class Parent < ActiveRecord::Base
      has_many :children, dependent: :destroy

      # Generated by first running:  Parent.suggest_template(1, true, true)
      # And then just added :children_firstname to the uniques,
      # and the six column aliases in the :as portion.
      IMPORT_TEMPLATE = {
        uniques: [:firstname, :children_firstname],
        required: [],
        all: [:firstname, :lastname, :address,
          { children: [:firstname, :lastname, :dateofbirth] }],
        # An alias for each incoming column
        as: {
              'parent_1_firstname' => 'Firstname',
              'parent_1_lastname' => 'Lastname',
              'address' => 'Address',
              'childfirstname' => 'Children Firstname',
              'childlastname' => 'Children Lastname',
              'childdateofbirth' => 'Children Dateofbirth'
            }
      }.freeze

      def self.import(file)
        df_import(file)
      end
    end

    unload_class('Child')
    class Child < ActiveRecord::Base
      belongs_to :parent
    end
  end

  before(:each) do
    Parent.destroy_all
  end

  it 'should be able to import from CSV data' do
    csv_in = <<~CSV
      parent_1_firstname,parent_1_lastname,address,childfirstname,childlastname,childdateofbirth
      John,Wilson,68 Bell Road,Jessica,Wilson,2002-11-11
      John,Wilson,68 Bell Road,Josh,Wilson,2006-10-01
    CSV
    child_info_csv = CSV.new(csv_in)

    # Import CSV data
    # ---------------
    expect { Parent.import(child_info_csv) }.not_to raise_error

    parents = Parent.order(:id).pluck(:firstname, :lastname, :address)
    expect(parents.count).to eq(1)
    expect(parents).to eq([['John', 'Wilson', '68 Bell Road']])

    children = Child.order(:id).pluck(:firstname, :lastname, :dateofbirth)
    expect(children.count).to eq(2)
    expect(children).to eq(
      [
        ['Jessica', 'Wilson', Date.new(2002, 11, 11)],
        ['Josh', 'Wilson', Date.new(2006, 10, 1)]
      ]
    )

    # Export current data to CSV
    # --------------------------
    # Using #df_export, an array is returned, which is easily converted back to CSV
    exported_csv = CSV.generate(force_quotes: false) do |csv_out|
      Parent.df_export.each { |row| csv_out << row }
    end
    # The generated CSV exactly matches the original which we started with
    expect(exported_csv).to eq(csv_in)
  end

  it 'should be able to import from CSV data having a different column order' do
    csv_in = <<~CSV
      childdateofbirth,childfirstname,childlastname,address,parent_1_firstname,parent_1_lastname
      2002-11-11,Jessica,Wilson,68 Bell Road,John,Wilson
      2006-10-01,Josh,Wilson,68 Bell Road,John,Wilson
    CSV
    child_info_csv = CSV.new(csv_in)

    # Import CSV data
    # ---------------
    expect { Parent.import(child_info_csv) }.not_to raise_error

    parents = Parent.order(:id).pluck(:firstname, :lastname, :address)
    expect(parents.count).to eq(1)
    expect(parents).to eq([['John', 'Wilson', '68 Bell Road']])

    children = Child.order(:id).pluck(:firstname, :lastname, :dateofbirth)
    expect(children.count).to eq(2)
    expect(children).to eq(
      [
        ['Jessica', 'Wilson', Date.new(2002, 11, 11)],
        ['Josh', 'Wilson', Date.new(2006, 10, 1)]
      ]
    )
  end

  it 'should allow for some columns to be missing' do
    # As long as we're not missing the uniques columns, we're fine
    csv_in = <<~CSV
      childdateofbirth,childfirstname,address,parent_1_firstname
      2002-11-11,Jessica,68 Bell Road,John
      2006-10-01,Josh,68 Bell Road,John
    CSV
    child_info_csv = CSV.new(csv_in)

    # Import CSV data
    # ---------------
    expect { Parent.import(child_info_csv) }.not_to raise_error

    parents = Parent.order(:id).pluck(:firstname, :lastname, :address)
    expect(parents.count).to eq(1)
    expect(parents).to eq([['John', nil, '68 Bell Road']])

    children = Child.order(:id).pluck(:firstname, :lastname, :dateofbirth)
    expect(children.count).to eq(2)
    expect(children).to eq(
      [
        ['Jessica', nil, Date.new(2002, 11, 11)],
        ['Josh', nil, Date.new(2006, 10, 1)]
      ]
    )
  end

  it 'should not create duplicate data even when there are duplicate rows' do
    # Consider that uniqueness is derived from the parent and child's first name, and not their last name.
    # In the case of Josh Barvis, that row is INSERTed, and then UPDATEd when the next row comes along,
    # effectively saying that Josh's last name is Wilson and not Barvis.
    csv_in = <<~CSV
      childdateofbirth,childfirstname,childlastname,address,parent_1_firstname,parent_1_lastname
      2002-11-11,Jessica,Wilson,68 Bell Road,John,Wilson
      2006-10-01,Josh,Barvis,68 Bell Road,John,Barvis
      2006-10-01,Josh,Wilson,68 Bell Road,John,Wilson
    CSV
    child_info_csv = CSV.new(csv_in)

    # Import CSV data
    # ---------------
    expect { Parent.import(child_info_csv) }.not_to raise_error

    parents = Parent.order(:id).pluck(:firstname, :lastname, :address)
    expect(parents.count).to eq(1)
    expect(parents).to eq([['John', 'Wilson', '68 Bell Road']])

    children = Child.order(:id).pluck(:firstname, :lastname, :dateofbirth)
    expect(children.count).to eq(2)
    expect(children).to eq(
      [
        ['Jessica', 'Wilson', Date.new(2002, 11, 11)],
        ['Josh', 'Wilson', Date.new(2006, 10, 1)]
      ]
    )
  end
end
