=begin
 This file is part of Hopsworks
 Copyright (C) 2019, Logical Clocks AB. All rights reserved

 Hopsworks 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 (at your option) any later version.

 Hopsworks 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 <https://www.gnu.org/licenses/>.
=end
module ProvStateHelper
  def project_index_cleanup(session_email)
    reset_session
    with_admin_session

    #pp "cleaning up indices"
    tries = 0
    loop do
      query = "#{ENV['HOPSWORKS_TESTING']}/test/provenance/cleanup?size=100"
      pp "#{query}" if @debugOpt == true
      result = post "#{query}"
      expect_status_details(200)
      parsed_result = JSON.parse(result)
      tries = tries + 1
      break if parsed_result["result"] == 0
      break if tries == 10
    end
    expect(tries).to be <= 10
    reset_session
    create_session(session_email, "Pass123")
  end

  def setup_cluster_prov(provenance_type, prov_archive_size)
    reset_session
    with_admin_session
    old_provenance_type = getVar("provenance_type")["value"]
    old_provenance_archive_size = getVar("provenance_archive_size")["value"]

    setVar("provenance_type", provenance_type) if (old_provenance_type != provenance_type)
    setVar("provenance_archive_size", prov_archive_size) if (old_provenance_archive_size != prov_archive_size)

    new_provenance_type = getVar("provenance_type")["value"]
    expect(new_provenance_type).to eq provenance_type
    new_provenance_archive_size = getVar("provenance_archive_size")["value"]
    expect(new_provenance_archive_size).to eq prov_archive_size

    reset_session
    return old_provenance_type, old_provenance_archive_size
  end

  def restore_cluster_prov(provenance_type, prov_archive_size, old_provenance_type, old_prov_archive_size)
    @cookies = with_admin_session

    setVar("provenance_type", old_provenance_type) if (old_provenance_type != provenance_type)
    setVar("provenance_archive_size", old_prov_archive_size) if (old_prov_archive_size != prov_archive_size)

    @cookies = nil
  end

  def prov_create_dir(project, dirname)
    create_dir(project, dirname, query: "")
    expect_status_details(201)
  end

  def prov_delete_dir(project, path)
    delete "#{ENV['HOPSWORKS_API']}/project/#{project[:id]}/dataset/#{path}"
    expect_status_details(204)
  end

  def prov_create_experiment(project, experiment_name) 
    #pp "create experiment #{experiment_name} in project #{project[:projectname]}"
    prov_create_dir(project, "Experiments/#{experiment_name}")
  end

  def prov_create_model(project, model_name) 
    prov_create_dir(project, "#{prov_model_path(model_name)}")
  end

  def prov_create_model_version(project, model_name, model_version) 
    prov_create_dir(project, "#{prov_model_version_path(model_name, model_version)}")
  end

  def prov_model_path(model_name)
    "Models/#{model_name}"
  end

  def prov_model_version_path(model_name, model_version)
    "#{prov_model_path(model_name)}/#{model_version}"
  end

  def prov_create_td(project, td_name, td_version) 
    #pp "create training dataset #{td_name}_#{td_version} in project #{project[:projectname]}"
    training_datasets = "#{project[:projectname]}_Training_Datasets"
    prov_create_dir(project, "#{training_datasets}/#{td_name}_#{td_version}")
  end

  def prov_delete_experiment(project, experiment_name) 
    #pp "delete experiment #{experiment_name} in project #{project[:projectname]}"
    experiments = "Experiments"
    prov_delete_dir(project, "#{experiments}/#{experiment_name}")
  end

  def prov_delete_model(project, model_name) 
    #pp "delete model #{model_name} in project #{project[:projectname]}"
    models = "Models"
    prov_delete_dir(project, "#{models}/#{model_name}")
  end

  def prov_delete_td(project, td_name, td_version) 
    #pp "delete training dataset #{td_name}_#{td_version} in project #{project[:projectname]}"
    training_datasets = "#{project[:projectname]}_Training_Datasets"
    prov_delete_dir(project, "#{training_datasets}/#{td_name}_#{td_version}")
  end

  def prov_experiment_id(experiment_name)
    "#{experiment_name}"
  end

  def prov_model_id(model_name, model_version)
    "#{model_name}_#{model_version}"
  end

  def prov_td_id(td_name, td_version)
    "#{td_name}_#{td_version}"
  end

  def prov_get_td_record(project, td_name, td_version) 
    training_datasets = "#{project[:projectname]}_Training_Datasets"
    training_dataset = prov_td_id(td_name, td_version)
    FileProv.where("project_name": project["projectname"], "i_parent_name": training_datasets, "i_name": training_dataset)
  end

  def prov_get_experiment_record(project, experiment_name) 
    experiment_parent = "Experiments"
    FileProv.where("project_name": project["projectname"], "i_parent_name": experiment_parent, "i_name": experiment_name)
  end

  def prov_add_app_states1(app_id, user)
    timestamp = Time.now
    AppProv.create(id: app_id, state: "null", timestamp: timestamp, name: app_id, user: user, submit_time: timestamp-10, start_time: timestamp-5, finish_time: 0)
    AppProv.create(id: app_id, state: "null", timestamp: timestamp+5, name: app_id, user: user, submit_time: timestamp-10, start_time: timestamp-5, finish_time: 0)
  end
  def prov_add_app_states2(app_id, user)
    timestamp = Time.now
    AppProv.create(id: app_id, state: "null", timestamp: timestamp, name: app_id, user: user, submit_time: timestamp-10, start_time: timestamp-5, finish_time: 0)
    AppProv.create(id: app_id, state: "null", timestamp: timestamp+5, name: app_id, user: user, submit_time: timestamp-10, start_time: timestamp-5, finish_time: 0)
    AppProv.create(id: app_id, state: "FINISHED", timestamp: timestamp+10, name: app_id, user: user, submit_time: timestamp-10, start_time: timestamp-5, finish_time: timestamp+50)
  end

  def prov_check_experiment3(experiments, experiment_id, current_state)
    experiment = experiments.select { |e| e["mlId"] == experiment_id }
    expect(experiment.length).to eq 1

    #pp experiment[0]["appState"]
    expect(experiment[0]["appState"]["currentState"]).to eq current_state
  end

  def prov_check_asset_with_id(assets, asset_id) 
    asset = assets.select {|a| a["mlId"] == asset_id }
    expect(asset.length).to eq 1
    #pp asset
  end 

  def prov_check_asset_with_xattrs(assets, asset_id, xattrs)
    asset = assets.select {|a| a["mlId"] == asset_id }
    expect(asset.length).to eq 1
    #pp model
    expect(asset[0]["xattrs"]["entry"].length).to eq xattrs.length
    xattrs.each do |key, value|
      #pp model[0]["xattrs"]["entry"]
      xattr = asset[0]["xattrs"]["entry"].select do |e|
        e["key"] == key && e["value"] == value
      end
      expect(xattr.length).to eq 1
      #pp xattr
    end
  end 

  def get_ml_asset_in_project(project, ml_type, with_app_state, expected)
    resource = "#{ENV['HOPSWORKS_API']}/project/#{project[:id]}/provenance/states"
    query_params = "?filter_by=ML_TYPE:#{ml_type}"
    if with_app_state
      query_params = query_params + "&expand=APP"
    end
    pp "#{resource}#{query_params}" if defined?(@debugOpt) && @debugOpt == true
    result = get "#{resource}#{query_params}"
    expect_status_details(200)
    parsed_result = JSON.parse(result)
    pp parsed_result if defined?(@debugOpt) && @debugOpt == true
    expect(parsed_result["items"].length).to eq expected
    expect(parsed_result["count"]).to eq expected
    parsed_result
  end

  def ml_type(type)
    ->(url) { "#{url}filter_by=ML_TYPE:#{type}" }
  end

  def get_ml_asset_in_project_page(project, ml_type, with_app_state, offset, limit)
    resource = "#{ENV['HOPSWORKS_API']}/project/#{project[:id]}/provenance/states"
    query_params = "?filter_by=ML_TYPE:#{ml_type}&offset=#{offset}&limit=#{limit}"
    if with_app_state
      query_params = query_params + "&expand=APP"
    end
    # pp "#{resource}#{query_params}"
    result = get "#{resource}#{query_params}"
    expect_status_details(200)
    parsed_result = JSON.parse(result)
    parsed_result
  end

  def check_no_ml_asset_by_id(project, ml_type, ml_id, with_app_state)
    resource = "#{ENV['HOPSWORKS_API']}/project/#{project[:id]}/provenance/states"
    query_params = "?filter_by=ML_TYPE:#{ml_type}&filter_by=ML_ID:#{ml_id}"
    if with_app_state
      query_params = query_params + "&expand=APP"
    end
    # pp "#{resource}#{query_params}"
    result = get "#{resource}#{query_params}"
    expect_status_details(200)
    parsed_result = JSON.parse(result)
    expect(parsed_result["items"].length).to eq 0
    expect(parsed_result["count"]).to eq 0
  end

  def get_ml_asset(project, ml_type: nil, ml_id: nil)
    resource = "#{ENV['HOPSWORKS_API']}/project/#{project[:id]}/provenance/states"
    query_params = nil
    query_params = append_to_query(query_params, "filter_by=ML_TYPE:#{ml_type}") unless ml_type.nil?
    query_params = append_to_query(query_params, "filter_by=ML_ID:#{ml_id}") unless ml_id.nil?
    get "#{resource}#{query_params}"
    expect_status_details(200)
    json_body
  end

  def get_ml_asset_by_id(project, ml_type, ml_id, with_app_state)
    resource = "#{ENV['HOPSWORKS_API']}/project/#{project[:id]}/provenance/states"
    query_params = "?filter_by=ML_TYPE:#{ml_type}&filter_by=ML_ID:#{ml_id}"
    if with_app_state
      query_params = query_params + "&expand=APP"
    end
    # pp "#{resource}#{query_params}"
    result = get "#{resource}#{query_params}"
    expect_status_details(200)
    parsed_result = JSON.parse(result)
    expect(parsed_result["items"].length).to eq 1
    expect(parsed_result["count"]).to eq 1
    parsed_result["items"][0]
  end

  def get_ml_asset_like_name(project, ml_type, term)
    resource = "#{ENV['HOPSWORKS_API']}/project/#{project[:id]}/provenance/states"
    query_params = "?filter_by=ML_TYPE:#{ml_type}&filter_by=FILE_NAME_LIKE:#{term}"
    # pp "#{resource}#{query_params}"
    result = get "#{resource}#{query_params}"
    expect_status_details(200)
    parsed_result = JSON.parse(result)
    parsed_result["items"]
  end

  def get_ml_in_create_range(project, ml_type, from, to, expected)
    resource = "#{ENV['HOPSWORKS_API']}/project/#{project[:id]}/provenance/states"
    query_params = "?filter_by=ML_TYPE:#{ml_type}&filter_by=CREATE_TIMESTAMP_LT:#{to}&filter_by=CREATE_TIMESTAMP_GT:#{from}"
    # pp "#{resource}#{query_params}"
    result = get "#{resource}#{query_params}"
    expect_status_details(200)
    parsed_result = JSON.parse(result)
    #pp parsed_result
    expect(parsed_result["items"].length).to eq expected
    expect(parsed_result["count"]).to eq expected
    parsed_result["items"]
  end

  def get_ml_asset_by_xattr(project, ml_type, xattr_key, xattr_val)
    resource = "#{ENV['HOPSWORKS_API']}/project/#{project[:id]}/provenance/states"
    query_params = "?filter_by=ML_TYPE:#{ml_type}&xattr_filter_by=#{xattr_key}:#{xattr_val}"
    # pp "#{resource}#{query_params}"
    result = get "#{resource}#{query_params}"
    expect_status_details(200)
    parsed_result = JSON.parse(result)
    parsed_result["items"]
  end

  def get_ml_asset_by_xattr_count(project, ml_type, xattr_key, xattr_val, count)
    resource = "#{ENV['HOPSWORKS_API']}/project/#{project[:id]}/provenance/states"
    query_params = "?filter_by=ML_TYPE:#{ml_type}&xattr_filter_by=#{xattr_key}:#{xattr_val}&return_type=COUNT"
    pp "#{resource}#{query_params}" if defined?(@debugOpt) && @debugOpt
    result = get "#{resource}#{query_params}"
    expect_status_details(200)
    parsed_result = JSON.parse(result)
    pp "#{parsed_result}" if defined?(@debugOpt) && @debugOpt
    expect(parsed_result["count"]).to eq count
    parsed_result["count"]
  end

  def get_ml_asset_like_xattr(project, ml_type, xattr_key, xattr_val)
    resource = "#{ENV['HOPSWORKS_API']}/project/#{project[:id]}/provenance/states"
    query_params = "?filter_by=ML_TYPE:#{ml_type}&xattr_like=#{xattr_key}:#{xattr_val}"
    # pp "#{resource}#{query_params}"
    result = get "#{resource}#{query_params}"
    expect_status_details(200)
    parsed_result = JSON.parse(result)
    parsed_result["items"]
  end
    
  def get_ml_td_count_using_feature_project(project, feature_name) 
    resource = "#{ENV['HOPSWORKS_API']}/project/#{project[:id]}/provenance/states"
    query_params = "?filter_by=ML_TYPE:TRAINING_DATASET&xattr_filter_by=features.name:#{feature_name}&return_type=COUNT"
    # pp "#{resource}#{query_params}"
    result = get "#{resource}#{query_params}"
    expect_status_details(200)
    parsed_result = JSON.parse(result)
    parsed_result["items"]
  end

  def get_ml_td_count_using_feature_global(feature_name) 
    resource = "#{ENV['HOPSWORKS_API']}/provenance/states"
    query_params = "?filter_by=ML_TYPE:TRAINING_DATASET&xattr_filter_by==features.name:#{feature_name}&return_type=COUNT"
    # pp "#{resource}#{query_params}"
    result = get "#{resource}#{query_params}"
    expect_status_details(200)
    parsed_result = JSON.parse(result)
    parsed_result["items"]
  end

  def attach_app_id_xattr(project, inode_id, app_id)
    target = "#{ENV['HOPSWORKS_TESTING']}/test/project/#{project[:id]}/provenance/xattr"
    param = "?inodeId=#{inode_id}&xattrName=app_id&xattrValue=#{app_id}"
    # pp "#{target}#{param}"
    result = post "#{target}#{param}"
    expect_status_details(200)
  end

  def prov_with_retries(max_retries, wait_time, expected_code, &block)
    response = nil
    max_retries.times {
      response = block.call
      break if response.code == expected_code
      sleep(wait_time)
    }
    expect(response.code).to eq expected_code
    return response
  end

  def prov_test_add_xattr(project, inode_id, xattr_name, xattr_val)
    pp  "#{ENV['HOPSWORKS_TESTING']}/test/project/#{project[:id]}/provenance/xattr?inodeId=#{inode_id}&xattrName=#{xattr_name}&xattrValue=#{xattr_val}" if defined?(@debugOpt) && @debugOpt
    post "#{ENV['HOPSWORKS_TESTING']}/test/project/#{project[:id]}/provenance/xattr?inodeId=#{inode_id}&xattrName=#{xattr_name}&xattrValue=#{xattr_val}"
    expect_status_details(200)
  end

  def prov_test_remove_xattr(project, inode_id, xattr_name)
    pp  "#{ENV['HOPSWORKS_TESTING']}/test/project/#{project[:id]}/provenance/xattr?inodeId=#{inode_id}&xattrName=#{xattr_name}" if defined?(@debugOpt) && @debugOpt
    delete "#{ENV['HOPSWORKS_TESTING']}/test/project/#{project[:id]}/provenance/xattr?inodeId=#{inode_id}&xattrName=#{xattr_name}"
    expect_status_details(200)
  end
end