class Team < ActiveRecord::Base
  include SearchableModel

  # Not really MVC-compliant, but we just use it for logger
  # output in space_taken related functions
  include ActionView::Helpers::NumberHelper

  auto_strip_attributes :name, :description, nullify: false
  validates :name,
            length: { minimum: Constants::NAME_MIN_LENGTH,
                      maximum: Constants::NAME_MAX_LENGTH }
  validates :description, length: { maximum: Constants::TEXT_MAX_LENGTH }
  validates :space_taken, presence: true

  belongs_to :created_by, :foreign_key => 'created_by_id', :class_name => 'User'
  belongs_to :last_modified_by, foreign_key: 'last_modified_by_id', class_name: 'User'
  has_many :user_teams, inverse_of: :team, dependent: :destroy
  has_many :users, through: :user_teams
  has_many :samples, inverse_of: :team
  has_many :samples_tables, inverse_of: :team, dependent: :destroy
  has_many :sample_groups, inverse_of: :team
  has_many :sample_types, inverse_of: :team
  has_many :projects, inverse_of: :team
  has_many :custom_fields, inverse_of: :team
  has_many :protocols, inverse_of: :team, dependent: :destroy
  has_many :protocol_keywords, inverse_of: :team, dependent: :destroy
  has_many :tiny_mce_assets, inverse_of: :team, dependent: :destroy
  # Based on file's extension opens file (used for importing)
  def self.open_spreadsheet(file)
    filename = file.original_filename
    file_path = file.path

    if file.class == Paperclip::Attachment and file.is_stored_on_s3?
      fa = file.fetch
      file_path = fa.path
    end

    case File.extname(filename)
    when ".csv" then
      Roo::CSV.new(file_path, extension: :csv)
    when ".tdv" then
      Roo::CSV.new(file_path, nil, :ignore, csv_options: {col_sep: "\t"})
    when ".txt" then
      # This assumption is based purely on biologist's habits
      Roo::CSV.new(file_path, csv_options: {col_sep: "\t"})
    when ".xls" then
      Roo::Excel.new(file_path)
    when ".xlsx" then
      Roo::Excelx.new(file_path)
    else
      raise TypeError
    end
  end

  def search_users(query = nil)
    a_query = "%#{query}%"
    users.where.not(confirmed_at: nil)
         .where('full_name ILIKE ? OR email ILIKE ?', a_query, a_query)
  end

  # Imports samples into db
  # -1 == sample_name,
  # -2 == sample_type,
  # -3 == sample_group
  # TODO: use constants
  def import_samples(sheet, mappings, user)
    errors = []
    nr_of_added = 0
    total_nr = 0

    # First let's query for all custom_fields we're refering to
    custom_fields = []
    sname_index = -1
    stype_index = -1
    sgroup_index = -1
    mappings.each.with_index do |(k, v), i|
      if v == "-1"
        # Fill blank space, so our indices stay the same
        custom_fields << nil
        sname_index = i
      elsif v == "-2"
        custom_fields << nil
        stype_index = i
      elsif v == "-3"
        custom_fields << nil
        sgroup_index =  i
      else
        cf = CustomField.find_by_id(v)

        # Even if doesn't exist we add nil value in order not to destroy our
        # indices
        custom_fields << cf
      end
    end

    # Now we can iterate through sample data and save stuff into db
    (2..sheet.last_row).each do |i|
      error = []
      total_nr += 1

      sample = Sample.new(
        name: sheet.row(i)[sname_index],
        team_id: id,
        user: user
      )

      if sample.save
        sheet.row(i).each.with_index do |value, index|
          # We need to have sample saved before messing with custom fields (they
          # need sample id)
          if index == stype_index
            stype = SampleType.where(name: value, team_id: id).take

            if stype
              sample.sample_type = stype
            else
              sample.create_sample_type(
                name: value,
                team_id: id
              )
            end
            sample.save
          elsif index == sgroup_index
            sgroup = SampleGroup.where(name: value, team_id: id).take

            if sgroup
              sample.sample_group = sgroup
            else
              sample.create_sample_group(
                name: value,
                team_id: id
              )
            end
            sample.save
          elsif value and mappings[index.to_s].strip.present? and index != sname_index
            if custom_fields[index]
              # we're working with CustomField
              scf = SampleCustomField.new(
                sample_id: sample.id,
                custom_field_id: custom_fields[index].id,
                value: value
              )

              if !scf.save
                error << scf.errors.messages
              end
            else
              # This custom_field does not exist
              error << {"#{mappings[index]}": "Does not exists"}
            end
          end
        end
      else
        error << sample.errors.messages
      end
      if error.present?
        errors << { "#{i}": error}
      else
        nr_of_added += 1
      end
    end

    if errors.count > 0 then
      return {
        status: :error,
        errors: errors,
        nr_of_added: nr_of_added,
        total_nr: total_nr
      }
    else
      return {
        status: :ok,
        nr_of_added: nr_of_added,
        total_nr: total_nr
      }
    end
  end

  def to_csv(samples, headers)
    require "csv"

    # Parse headers (magic numbers should be refactored - see
    # sample-datatable.js)
    header_names = []
    headers.each do |header|
      if header == "-1"
        header_names << I18n.t("samples.table.sample_name")
      elsif header == "-2"
        header_names << I18n.t("samples.table.sample_type")
      elsif header == "-3"
        header_names << I18n.t("samples.table.sample_group")
      elsif header == "-4"
        header_names << I18n.t("samples.table.added_by")
      elsif header == "-5"
        header_names << I18n.t("samples.table.added_on")
      else
        cf = CustomField.find_by_id(header)

        if cf
          header_names << cf.name
        else
          header_names << nil
        end
      end
    end

    CSV.generate do |csv|
      csv << header_names
      samples.each do |sample|
        sample_row = []
        headers.each do |header|
          if header == "-1"
            sample_row << sample.name
          elsif header == "-2"
            sample_row << (sample.sample_type.nil? ? I18n.t("samples.table.no_type") : sample.sample_type.name)
          elsif header == "-3"
            sample_row << (sample.sample_group.nil? ? I18n.t("samples.table.no_group") : sample.sample_group.name)
          elsif header == "-4"
            sample_row << sample.user.full_name
          elsif header == "-5"
            sample_row << I18n.l(sample.created_at, format: :full)
          else
            scf = SampleCustomField.where(
              custom_field_id: header,
              sample_id: sample.id
            ).take

            if scf
              sample_row << scf.value
            else
              sample_row << nil
            end
          end
        end
        csv << sample_row
      end
    end
  end

  # Get all header fields for samples (used in importing for mappings - dropdowns)
  def get_available_sample_fields
    fields = {};

    # First and foremost add sample name
    fields["-1"] = I18n.t("samples.table.sample_name")
    fields["-2"] = I18n.t("samples.table.sample_type")
    fields["-3"] = I18n.t("samples.table.sample_group")

    # Add all other custom fields
    CustomField.where(team_id: id).order(:created_at).each do |cf|
      fields[cf.id] = cf.name
    end

    fields
  end

  # (re)calculate the space taken by this team
  def calculate_space_taken
    st = 0
    projects.includes(
      experiments: {
        my_modules: {
          protocols: { steps: :assets },
          results: { result_asset: :asset }
        }
      }
    ).find_each do |project|
      project.project_my_modules.find_each do |my_module|
        my_module.protocol.steps.find_each do |step|
          step.assets.find_each { |asset| st += asset.estimated_size }
          step.tiny_mce_assets.find_each { |tiny| st += tiny.estimated_size }
        end
        my_module.results.find_each do |result|
          st += result.asset.estimated_size if result.is_asset
          if result.is_text
            tiny_assets = TinyMceAsset.where(result_text: result.result_text)
            tiny_assets.find_each { |tiny| st += tiny.estimated_size }
          end
        end
      end
    end
    # project.experiments.each |experiment|
    self.space_taken = [st, Constants::MINIMAL_TEAM_SPACE_TAKEN].max
    Rails::logger.info "Team #{self.id}: " +
      "space (re)calculated to: " +
      "#{self.space_taken}B (#{number_to_human_size(self.space_taken)})"
  end

  # Take specified amount of bytes
  def take_space(space)
    orig_space = self.space_taken
    self.space_taken += space
    Rails::logger.info "Team #{self.id}: " +
      "space taken: " +
      "#{orig_space}B + #{space}B = " +
      "#{self.space_taken}B (#{number_to_human_size(self.space_taken)})"
  end

  # Release specified amount of bytes
  def release_space(space)
    orig_space = self.space_taken
    self.space_taken = [space_taken - space,
                        Constants::MINIMAL_TEAM_SPACE_TAKEN].max
    Rails::logger.info "Team #{self.id}: " +
      "space released: " +
      "#{orig_space}B - #{space}B = " +
      "#{self.space_taken}B (#{number_to_human_size(self.space_taken)})"
  end

  def protocol_keywords_list
    ProtocolKeyword.where(team: self).pluck(:name)
  end
end
