require File.expand_path('../../spec_helper', __FILE__)

RSpec.shared_examples 'a new project' do |copy_type|
  context "created through #{copy_type}" do
    before do
      @new_project = @project.create_project_template(copying_action: copy_type)
    end

    it 'with correct saved queries' do
      expect(@new_project.easy_queries.size).to eq(@project.easy_queries.size)

      @new_project.easy_queries.each do |query|
        expect(
          query.entities.map{|issue| issue.subject}
        ).to match_array(@origin_issues[query.name])
      end
    end

    it 'with correct page modules' do
      page = EasyPage.find_by(page_name: 'project-overview')
      modules = EasyPageZoneModule.where(easy_pages_id: page.id, entity_id: @new_project.id)
      modules.each do |zone_module|
        next unless zone_module.module_definition.query_module?

        settings = zone_module.settings#.with_indifferent_access

        name = settings.delete(:query_name)
        query = settings.delete(:type).constantize.new
        query.project = @new_project
        query.from_params(settings)

        expect(
          query.entities.map{|issue| issue.subject}
        ).to match_array(@origin_issues[name])
      end

    end
  end
end

RSpec.describe Project, :type => :model do

  # Keep all variables as instance in before block
  # RSpec 2 is problematic when you use let-before-shared_examples
  # + this behavior is deprecated in RSpec3
  context 'copying' do

    # Create project and assign versions and categories
    before do
      @project = FactoryGirl.create(:project, :with_categories, :with_milestones, number_of_issues: 10, number_of_versions: 3, number_of_issue_categories: 3)

      @project.issues[0].category = @project.issue_categories[0]
      @project.issues[1].category = @project.issue_categories[1]
      @project.issues[2].category = @project.issue_categories[2]

      @project.issues[3].fixed_version = @project.versions[0]
      @project.issues[4].fixed_version = @project.versions[1]
      @project.issues[5].fixed_version = @project.versions[2]

      @project.issues[6].category      = @project.issue_categories[0]
      @project.issues[6].fixed_version = @project.versions[0]

      @project.issues[7].category      = @project.issue_categories[1]
      @project.issues[7].fixed_version = @project.versions[2]

      @project.issues.each(&:save)
      @project
    end

    # Admin user for permission for all query filters
    before do
      @user = FactoryGirl.create(:user, :admin)
      logged_user(@user)
    end

    # Public project queries
    before do
      @queries = []

      query = EasyIssueQuery.new
      query.name = 'project_query_1'
      query.add_filter('fixed_version_id', '=', [@project.versions[0].id.to_s])
      @queries << query

      query = EasyIssueQuery.new
      query.name = 'project_query_2'
      query.add_filter('category_id', '=', [@project.issue_categories[0].id.to_s])
      @queries << query

      query = EasyIssueQuery.new
      query.name = 'project_query_3'
      query.add_filter('category_id', '=', [@project.issue_categories[0].id.to_s])
      query.add_filter('fixed_version_id', '=', [@project.versions[0].id.to_s])
      @queries << query

      @queries.each do |query|
        query.visibility = EasyQuery::VISIBILITY_PUBLIC
        query.project = @project
        query.save
      end
    end

    # Remember origin issue subjects
    before do
      @origin_issues = {}
      @project.easy_queries.each do |query|
        @origin_issues[query.name] = query.entities.map{|issue| issue.subject}
      end
      @origin_issues
    end

    # Create page modules, before(:all) is not supported in Rspec 3
    before do
      page = EasyPage.find_by(page_name: 'project-overview')
      zone = page.zones.first

      page_module = EasyPageModule.find_by_type('EpmIssueQuery')
      available_module = page.modules.where(easy_page_modules_id: page_module.id).first

      @queries.each do |query|
        settings = query.to_params.with_indifferent_access
        settings[:query_name] = query.name

        page_zone_module = EasyPageZoneModule.new(easy_pages_id: page.id, easy_page_available_zones_id: zone.id, easy_page_available_modules_id: available_module.id, entity_id: @project.id, tab: 1, settings: settings)
        page_zone_module.save!
      end
    end


    it_behaves_like 'a new project', :creating_template
    it_behaves_like 'a new project', :creating_project
    it_behaves_like 'a new project', :copying_project
  end

  context 'new project from template' do
    let(:parent_project) { FactoryGirl.create(:project) }
    let(:invalid_query) { FactoryGirl.build(:easy_issue_query,
          :project => parent_project,
          :filters => {'tracker_id' => {:values => [(Tracker.last.id + 1).to_s], :operator => '='}})
    }

    context 'admin', :logged => :admin do
      it 'should create' do
        parent_project.reload
        expect{parent_project.project_from_template(nil, :name => parent_project.name)}.to change(Project, :count).by(1)
      end

      it 'should create with invalid query' do
        allow(invalid_query).to receive(:valid?).and_return(false)

        invalid_query.save(:validate => false)
        expect(invalid_query.valid?).to be false
        parent_project.reload
        expect{parent_project.project_from_template(nil, :name => parent_project.name)}.to change(Project, :count).by(1)
      end
    end

    context 'regular user', :logged => true do
      let!(:parent_project_with_member) { FactoryGirl.create(:project, :members => [User.current]) }
      let(:role) { FactoryGirl.create(:role) }

      before(:each) do
        role = Role.non_member
        role.add_permission!(:add_project)
        role.reload
        parent_project_with_member.reload
        User.current.reload
      end

      it 'member' do
        with_settings(:new_project_user_role_id => User.current.roles.last.id.to_s) do
          expect{parent_project_with_member.project_from_template(nil, :name => parent_project_with_member.name)}.to change(Project, :count).by(1)
          expect(User.current.member_of?(Project.last)).to be true
        end
      end

      it 'non member' do
        with_settings(:new_project_user_role_id => role.id.to_s) do
          expect{parent_project_with_member.project_from_template(nil, :name => parent_project_with_member.name)}.to change(Project, :count).by(1)
          expect(User.current.member_of?(Project.last)).to be true
        end
      end
    end

    context 'with activities', :logged => :admin do
      let(:child_project) { FactoryGirl.create(:project) }
      let(:activity) { FactoryGirl.create(:time_entry_activity, projects: [parent_project]) }

      it 'from parent' do
        parent_project.project_time_entry_activities = []
        activity
        p = child_project.project_from_template(parent_project.id, :name => child_project.name, :inherit_time_entry_activities => true)
        p.copy_time_entry_activities_from_parent
        expect(p.project_time_entry_activities.first.id).to eq(activity.id)
      end
    end
  end

  it 'close project' do
    p = FactoryGirl.create(:project)
    updated_on = p.updated_on
    with_time_travel(1.day) do
      p.close
      p.reload
      expect(p.updated_on).not_to eq(updated_on)
    end
  end

  context 'available trackers', :logged => true do
    let(:project) { FactoryGirl.create(:project, :members => [User.current])}
    it 'return trackers' do
      role = Role.non_member
      role.add_permission!(:add_issue)
      role.reload
      expect(project.available_trackers.count).to eq(2)
    end
  end

  context 'issue sums' do
    let!(:project1) { FactoryGirl.create(:project, number_of_issues: 0, number_of_subprojects: 0, enabled_module_names: ['issue_tracking', 'time_tracking']) }
    let!(:project2) { FactoryGirl.create(:project, number_of_issues: 0, number_of_subprojects: 0, parent_id: project1.id, enabled_module_names: ['issue_tracking', 'time_tracking']) }
    let!(:issue1) { FactoryGirl.create(:issue, subject: 'issue1', project_id: project1.id, estimated_hours: 10, done_ratio: 10) }
    let!(:issue2) { FactoryGirl.create(:issue, subject: 'issue2', project_id: project2.id, estimated_hours: 100, done_ratio: 100) }
    let!(:activity) { FactoryGirl.create(:time_entry_activity, projects: [project1, project2]) }
    let!(:time_entry1) { FactoryGirl.create(:time_entry, issue_id: issue1.id, activity_id: activity.id, project_id: project1.id, hours: 5) }
    let!(:time_entry2) { FactoryGirl.create(:time_entry, issue_id: issue2.id, activity_id: activity.id, project_id: project2.id, hours: 10) }

    before(:each) { project1.reload; project2.reload }

    ['0', '1'].each do |display_subprojects_issues|
      it "computes sum of issues estimated hours#{(display_subprojects_issues == '1') ? ' with display_subprojects_issues setting' : ''}" do
        with_settings(display_subprojects_issues: display_subprojects_issues) do
          [true, false].each do |only_self|
            sum = project1.sum_of_issues_estimated_hours(only_self)
            expect(sum).to be_kind_of(Numeric)
          end
        end
      end

      ['weighted', 'time_spending', ''].each do |formula|
        it "computes completed percent #{formula.blank? ? '' : "with formula #{formula}"}#{(display_subprojects_issues == '1') ? ' and display_subprojects_issues setting' : ''}" do
          with_settings(display_subprojects_issues: display_subprojects_issues) do
            with_easy_settings(project_completion_formula: formula) do
              [true, false].each do |include_subprojects|
                sum = project1.completed_percent(include_subprojects: include_subprojects)
                expect(sum).to be_kind_of(Numeric)
                is_with_sub_projects = include_subprojects && display_subprojects_issues == '1'
                case formula
                when 'weighted' # (SUM(done_ratio / 100 * estimated_hours) / SUM(estimated_hours) * 100)
                  expect(sum.round).to eq(is_with_sub_projects ? 92 : 10)
                when 'time_spending' # (SUM of time entries' hours / SUM of estimated hours) * 100
                  expect(sum.round).to eq(is_with_sub_projects ? 14 : 50)
                when '' # (SUM of done ratio / COUNT issues)
                  expect(sum.round).to eq(is_with_sub_projects ? 55 : 10)
                end
              end
            end
          end
        end
      end
    end
  end

  context 'indicator value' do
    let(:project) { FactoryGirl.create(:project, :easy_due_date => Date.today + 1.day) }
    let(:subproject) { FactoryGirl.create(:project, :status => 5, :parent_id => project.id, :easy_due_date => Date.today - 1.day) }

    it 'disregards closed subprojects' do
      with_settings(:display_subprojects_issues => true) do
        expect(project.easy_indicator).to eq(20)
      end
    end
  end

  context 'project activity roles', :logged => :admin do
    let!(:project_activity_role) { FactoryGirl.create(:project_activity_role) }
    let(:project) { project_activity_role.project }
    let(:role) { project_activity_role.role }
    let(:role_activity) { project_activity_role.role_activity }

    it 'deletes dependencies after project destroy' do
      expect{project.destroy}.to change(ProjectActivityRole, :count).by(-1)
    end

    it 'deletes dependencies after role destroy' do
      expect{role.destroy}.to change(ProjectActivityRole, :count).by(-1)
    end

    it 'deletes dependencies after activity destroy' do
      expect{role_activity.destroy}.to change(ProjectActivityRole, :count).by(-1)
    end
  end

  context 'when created new or updated' do
    let(:project) { FactoryGirl.build(:project, :easy_start_date => Date.today, :easy_due_date => Date.today - 3.days) }

    it 'due date cannot be before start date' do
      with_easy_settings(:project_calculate_start_date => false, :project_calculate_due_date => false) do
        expect(project.valid?).to be false
        expect(project.errors[:easy_due_date]).to include(I18n.t(:due_date_after_start, :scope => [:activerecord, :errors, :messages]))
      end
    end
  end

  context 'allowed parents', :logged => true do
    let!(:project) { FactoryGirl.create(:project, :members => [User.current]) }
    let!(:project2) { FactoryGirl.create(:project, :members => [User.current]) }
    let!(:template) { FactoryGirl.create(:project, :easy_is_easy_template => true, :members => [User.current]) }
    let!(:template2) { FactoryGirl.create(:project, :easy_is_easy_template => true, :members => [User.current]) }

    it 'projects' do
      role = Role.non_member
      role.add_permission!(:create_subproject_from_template)
      role.reload; User.current.reload

      template.is_from_template = true
      parents = template.allowed_parents(nil, :force => :projects)
      expect(parents).to include(project)
      expect(parents).to include(project2)
      expect(parents).not_to include(template)
      expect(parents).not_to include(template2)
    end

    it 'templates' do
      role = Role.non_member
      role.add_permission!(:add_subprojects)
      role.reload; User.current.reload

      parents = template.allowed_parents(nil, :force => :templates)
      expect(parents).not_to include(project)
      expect(parents).not_to include(project2)
      expect(parents).not_to include(template)
      expect(parents).to include(template2)
    end
  end
end

