# frozen_string_literal: true

require "rails_helper"

RSpec.describe UsageController, type: :controller do
  before(:each) do
    @date = Date.today.last_month.end_of_month
    @org = create(:org, :organisation)
    @details = { "by_template": [stat_details], "using_template": [] }
    @plan_stat = create(:stat_created_plan, date: @date, org: @org, details: @details)
    @user_stat = create(:stat_joined_user, date: @date, org: @org)

    sign_in(create(:user, :super_admin, org: @org))
  end

  describe "GET /usage (aka index)" do
    before(:each) do
      get :index
    end
    it "assigns the correct user stats (@users_per_month)" do
      expect(assigns(:users_per_month)).to eql(obj_to_hash(obj: @user_stat))
    end
    it "assigns the correct plan stats" do
      expect(assigns(:plans_per_month)).to eql(obj_to_hash(obj: @plan_stat))
    end
    it "assigns the correct total users" do
      expect(assigns(:total_org_users)).to eql(@user_stat.count)
    end
    it "assigns the correct total plans" do
      expect(assigns(:total_org_plans)).to eql(@plan_stat.count)
    end
  end

  describe "POST /usage_plans_by_template" do
    before(:each) do
      # Skipping the prior month because it was already created above
      (2..12).each do |i|
        date = Date.today.months_ago(i).end_of_month
        details = { "by_template": [stat_details] }
        create(:stat_created_plan, date: date, org: @org, details: details)
      end
      @annual = StatCreatedPlan.all.order(date: :desc)
    end
    describe "test for each date range" do
      [1, 3, 6, 9, 12].each do |months|
        context "last #{months} months" do
          before(:each) do
            @date = Date.today.months_ago(months).end_of_month.strftime("%Y-%m-%d")
            post :plans_by_template, usage: { template_plans_range: @date }, format: :js
          end
          it "returns the expected data" do
            # Controller returns results in date ascending order so resort the
            # records after extracting the ones we want first
            expected = @annual[0..months - 1].sort { |a, b| a.date <=> b.date }
                                             .map { |stat| obj_to_hash(obj: stat) }
            expect(assigns(:plans_per_month)).to eql(expected.flatten)
          end
        end
      end
    end
  end

  describe "GET /usage_global_statistics" do
    before(:each) do
      get :global_statistics
    end
    it "assigns the correct csv data" do
      csvified_name = @org.name.include?(",") ? "\"#{@org.name}\"" : @org.name
      expected = "Org name,Total users,Total plans\n" +
                 "#{csvified_name},#{@user_stat.count},#{@plan_stat.count}\n"
      expect(response.content_type).to eq("text/csv")
      expect(response.body).to eql(expected)
    end
  end

  describe "POST /usage_filter" do
    before(:each) do
      @date2 = Date.today.months_ago(3).end_of_month.strftime("%Y-%m-%d")
      @user_stat2 = create(:stat_joined_user, date: @date2, org: @org)
      @plan_stat2 = create(:stat_created_plan, date: @date2, org: @org, details: @details)
    end

    context "date range specified" do
      before(:each) do
        @args =  {
          start_date: Date.today.months_ago(3).strftime("%Y-%m-%d"),
          end_date: @date2
        }
      end
      it "returns the correct values for users" do
        post :filter, usage: @args.merge({ topic: "users" }), format: :js
        expect(assigns(:ranged)).to eql(@user_stat2.count)
        expect(assigns(:total)).to eql(@user_stat2.count + @user_stat.count)
      end
      it "returns the correct values for plans" do
        post :filter, usage: @args.merge({ topic: "plans" }), format: :js
        expect(assigns(:ranged)).to eql(@plan_stat2.count)
        expect(assigns(:total)).to eql(@plan_stat2.count + @plan_stat.count)
      end
    end

    context "no date range specified" do
      it "returns the correct values for users" do
        post :filter, usage: { topic: "users" }, format: :js
        expect(assigns(:ranged)).to eql(@user_stat2.count + @user_stat.count)
        expect(assigns(:total)).to eql(@user_stat2.count + @user_stat.count)
      end
      it "returns the correct values for plans" do
        post :filter, usage: { topic: "plans" }, format: :js
        expect(assigns(:ranged)).to eql(@plan_stat2.count + @plan_stat.count)
        expect(assigns(:total)).to eql(@plan_stat2.count + @plan_stat.count)
      end
    end
  end

  describe "GET /usage_yearly_users" do
    before(:each) do
      get :yearly_users
    end
    it "assigns the correct csv data" do
      expected = "Month,No. Users joined\n" +
                 "#{@date.strftime("%b-%y")},#{@user_stat.count}\n" +
                 "Total,#{@user_stat.count}\n"
      expect(response.content_type).to eq("text/csv")
      expect(response.body).to eql(expected)
    end
  end

  describe "GET /usage_yearly_plans" do
    before(:each) do
      get :yearly_plans
    end
    it "assigns the correct csv data" do
      expected = "Month,No. Completed Plans\n" +
                 "#{@date.strftime("%b-%y")},#{@plan_stat.count}\n" +
                 "Total,#{@plan_stat.count}\n"
      expect(response.content_type).to eq("text/csv")
      expect(response.body).to eql(expected)
    end
  end

  describe "GET /usage_all_plans_by_template" do
    before(:each) do
      get :all_plans_by_template
    end
    it "assigns the correct csv data" do
      name = @details[:by_template].first[:name]
      count = @details[:by_template].first[:count]
      expected = "Date,#{name},Count\n" +
                 "#{@date.strftime("%b %Y")},#{count},#{@plan_stat.count}\n"
      expect(response.content_type).to eq("text/csv")
      expect(response.body).to eql(expected)
    end
  end

  def obj_to_hash(obj:)
    hash = { "count": obj.count, "date": obj.date.strftime("%Y-%m-%d") }
    hash["by_template"] = obj.details.fetch("by_template", []) if obj.details.present?
    hash["using_template"] = obj.details.fetch("using_template", []) if obj.details.present?
    [hash.to_json]
  end

  def stat_details
    { "name": Faker::Lorem.unique.sentence, "count": Faker::Number.number(digits: 2) }
  end
end
