# = Informations
#
# == License
#
# Ekylibre - Simple agricultural ERP
# Copyright (C) 2008-2009 Brice Texier, Thibaud Merigon
# Copyright (C) 2010-2012 Brice Texier
# Copyright (C) 2012-2014 Brice Texier, David Joulin
# Copyright (C) 2015-2023 Ekylibre SAS
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see http://www.gnu.org/licenses.
#
# == Table: activity_productions
#
#  activity_id            :integer(4)       not null
#  batch_planting         :boolean
#  campaign_id            :integer(4)
#  created_at             :datetime         not null
#  creator_id             :integer(4)
#  cultivable_zone_id     :integer(4)
#  custom_fields          :jsonb
#  custom_name            :string
#  headland_shape         :geometry({:srid=>4326, :type=>"geometry"})
#  id                     :integer(4)       not null, primary key
#  irrigated              :boolean          default(FALSE), not null
#  lock_version           :integer(4)       default(0), not null
#  nitrate_fixing         :boolean          default(FALSE), not null
#  number_of_batch        :integer(4)
#  predicated_sowing_date :date
#  provider               :jsonb            default("{}")
#  rank_number            :integer(4)       not null
#  reference_name         :string
#  season_id              :integer(4)
#  size_indicator_name    :string           not null
#  size_unit_name         :string
#  size_value             :decimal(19, 4)   not null
#  sowing_interval        :integer(4)
#  started_on             :date
#  starting_year          :integer(4)
#  state                  :string
#  stopped_on             :date
#  support_id             :integer(4)       not null
#  support_nature         :string
#  support_shape          :geometry({:srid=>4326, :type=>"multi_polygon"})
#  tactic_id              :integer(4)
#  technical_itinerary_id :integer(4)
#  updated_at             :datetime         not null
#  updater_id             :integer(4)
#  usage                  :string           not null
#
require 'test_helper'

class ActivityProductionTest < Ekylibre::Testing::ApplicationTestCase::WithFixtures
  test_model_actions

  test 'create' do
    activity = Activity.find_by(production_cycle: :annual, family: 'plant_farming')
    campaign = Campaign.of(2016)
    p = activity.productions.new(started_on: '2015-07-01', stopped_on: '2016-01-15', campaign: campaign, cultivable_zone: CultivableZone.first)
    assert p.save, p.errors.inspect
    p = activity.productions.new(started_on: '2015-07-01', stopped_on: '72016-01-15', campaign: campaign, cultivable_zone: CultivableZone.first)
    assert !p.save, p.errors.inspect
    p = activity.productions.new(started_on: '15-07-01', stopped_on: '2016-01-15', campaign: campaign, cultivable_zone: CultivableZone.first)
    assert p.save, p.errors.inspect
    p = activity.productions.new(started_on: '2017-07-01', stopped_on: '2016-01-15', campaign: campaign, cultivable_zone: CultivableZone.first)
    assert !p.save, p.errors.inspect
  end

  test 'harvest_yield returns a measure' do
    cultivable_zone = create(:cultivable_zone)
    activity_production = create(:activity_production, cultivable_zone: cultivable_zone)
    result = activity_production.harvest_yield(:grass, procedure_category: :harvesting,
                                                       size_indicator_name: :net_mass,
                                                       size_unit_name: :ton,
                                                       surface_unit_name: :hectare)
    assert_equal Measure, result.class
  end

  # assert_in_delta cause psql ST_Area([...]) ~= Charta::MultiPolygon.area
  test 'working zone area 8' do
    production = ActivityProduction.find(8).decorate
    assert_in_delta 21.56007722495018, production.working_zone_area(production.campaign).to_d, 0.0005
  end

  test 'working zone area 6' do
    production = ActivityProduction.find(6).decorate
    assert_in_delta 0.0, production.working_zone_area(production.campaign).to_d, 0.0005
  end

  test 'working zone area 9' do
    production = ActivityProduction.find(9).decorate
    assert_in_delta 33.454308011622075, production.working_zone_area(production.campaign).to_d, 0.0005
  end

  test 'working zone area 49' do
    production = ActivityProduction.find(49).decorate
    assert_in_delta 1.958770832919594, production.working_zone_area(production.campaign).to_d, 0.0005
  end

  test 'costs' do
    campaign = Campaign.of(2018)
    production = create(:corn_activity_production, campaign: campaign, started_on: Date.new(2018, 0o4, 0o1), stopped_on: Date.new(2018, 10, 15))
    intervention1 = create(:intervention, started_at: DateTime.new(2018, 4, 2), stopped_at: DateTime.new(2018, 4, 2) + 2.hours)
    target = create(
      :intervention_target,
      product: production.products.first,
      intervention: intervention1,
      imputation_ratio: 1
    )
    doer = create(
      :driver,
      product: Product.find(79),
      intervention: intervention1
    )
    input = create(
      :intervention_input,
      product: Matter.find(59),
      intervention: intervention1
    )
    intervention1.save
    intervention2 = create(:intervention, started_at: DateTime.new(2018, 5, 2), stopped_at: DateTime.new(2018, 5, 2) + 2.hours)
    target = create(
      :intervention_target,
      product: production.products.first,
      intervention: intervention2,
      imputation_ratio: 1
    )
    doer = create(
      :driver,
      product: Product.find(79),
      intervention: intervention2
    )
    input = create(
      :intervention_input,
      product: Matter.find(59),
      intervention: intervention2
    )
    intervention2.save
    assert_equal(
      (intervention1.costing.doers_cost + intervention2.costing.doers_cost),
      production.decorate.global_costs(campaign)[:doers]
    )
    assert_equal(
      (intervention1.costing.inputs_cost + intervention2.costing.inputs_cost),
      production.decorate.global_costs(campaign)[:inputs]
    )
  end

  test 'costs intervention on multiple productions' do
    campaign = Campaign.of(2018)
    production1 = create(:corn_activity_production, campaign: campaign, started_on: DateTime.new(2018, 1, 1), stopped_on: Date.new(2018, 10, 15))
    production2 = create(:lemon_activity_production, campaign: campaign, started_on: DateTime.new(2018, 1, 1), stopped_on: Date.new(2018, 10, 15))
    intervention = create(:intervention, started_at: DateTime.new(2018, 3, 2), stopped_at: DateTime.new(2018, 3, 2) + 2.hours)
    target1 = create(
      :intervention_target,
      product: production1.products.first,
      intervention: intervention,
      working_zone: production1.support_shape.to_rgeo
    )
    target2 = create(
      :intervention_target,
      product: production2.products.first,
      intervention: intervention,
      working_zone: production2.support_shape.to_rgeo
    )
    doer = create(
      :driver,
      product: Product.find(79),
      intervention: intervention
    )
    input = create(
      :intervention_input,
      product: Matter.find(59),
      intervention: intervention
    )
    intervention.save
    target1.reload
    target2.reload

    assert_equal(
      (intervention.costing.doers_cost * target1.imputation_ratio).round(2),
      production1.decorate.global_costs(campaign)[:doers]
    )
    assert_equal(
      (intervention.costing.inputs_cost * target1.imputation_ratio).round(2),
      production1.decorate.global_costs(campaign)[:inputs]
    )
    assert_equal(
      (intervention.costing.doers_cost * target2.imputation_ratio).round(2),
      production2.decorate.global_costs(campaign)[:doers]
    )
    assert_equal(
      (intervention.costing.inputs_cost * target2.imputation_ratio).round(2),
      production2.decorate.global_costs(campaign)[:inputs]
    )
  end
end
