class SiteServerFileBackup
  include Mongoid::Document
  include Mongoid::Timestamps
  PeriodsTypes=['daily','weekly','monthly']
  DefaultConf='/etc/rsnapshot.conf'
  belongs_to :site_server
  field :disable, type: Boolean, default: false
  field :backup_time, type: String, default: '04:15'
  field :path , type: String ,default: '/home/backup/orbit'
  field :period, type: Integer, default: 0 # 0 => daily , 1 => weekly , 2 => monthly
  field :retain_count, type: Integer, default: 7
  field :rsnapshot_conf_path, type: String, default: DefaultConf
  field :backup_dir, type: String, default: '/home/{{user_name}}'
  field :backup_prefix, type: String, default: 'localhost/'
  field :need_rewrite, type: Boolean, default: true

  before_create do
    unless @skip_callback
      other_rsnapshot_conf_path = self.class.where(:site_server_id=>self.site_server_id).pluck(:rsnapshot_conf_path)
      if other_rsnapshot_conf_path.include?(self.rsnapshot_conf_path)
        max_postfix = other_rsnapshot_conf_path.select{|s| s.start_with?('/etc/')}.map{|s| tmp = File.basename(s).split('.conf')[0].match(/\d+/); tmp ? tmp[0].to_i : 0}.max
        self.rsnapshot_conf_path = "#{self.rsnapshot_conf_path.split('.conf')[0]}#{max_postfix + 1}.conf"
      end
    end
  end
  before_save do
    unless @skip_callback
      if self.changed?
        self.need_rewrite = true
      end
    end
  end
  def to_crontab_script
    # minute , hour, day(of month, 1~31), month(1~12), day(of week,0~6)
    crontab_time = ['*', '*', '*', '*', '*']
    time = self.backup_time.split(':').map{|s| s.sub(/0(\d)/){$1}} rescue []
    crontab_time[0] = time[1].to_i.to_s rescue '0'
    crontab_time[1] = time[0].to_i.to_s rescue '0'
    if self.period == 1 #weekly
      crontab_time[4] = '0' #every Sunday
    elsif self.period == 2 #monthly
      crontab_time[2] = '1' #day 1 of every month
    end
    extra_arg = ''
    if self.rsnapshot_conf_path != DefaultConf
      extra_arg = " -c #{rsnapshot_conf_path} "
    end
    "#{crontab_time.join(' ')} /usr/bin/rsnapshot #{extra_arg}#{period_text}"
  end
  def period_text
    PeriodsTypes[self.period]
  end
  def retain_text
    "retain\t#{self.period_text}\t#{self.retain_count}"
  end
  def override_text(contents, org_regex, new_text)
    exist_flag = false
    contents = contents.gsub(org_regex).with_index do |s, i|
      if i == 0
        exist_flag = true
        "#{new_text}#{(s[-1] == "\n" ? "\n" : '')}"
      else
        "##{s}"
      end
    end
    unless exist_flag
      contents += "\n#{new_text}"
    end
    contents
  end
  def gsub_rsnapshot_conf(contents)
    contents = self.override_text(contents, /^retain\s+[^\n]*(\n|$)/m, self.retain_text)
    contents = self.override_text(contents, /^snapshot_root\s+[^\n]*(\n|$)/m, self.snapshot_root)
    contents = self.override_text(contents, /^backup\s+[^\n]*(\n|$)/m, self.backup_path)
  end
  def backup_path
    home_dir = self.backup_dir
    if home_dir.include?('{{user_name}}')
      user_name = self.site_server.account
      home_dir = home_dir.sub('{{user_name}}', user_name)
      self.backup_dir = home_dir
      self.save
    end
    backup_text = "backup\t#{home_dir}/\t#{self.backup_prefix}"
    extra_exclude_path = []
    if self.class.class_variable_defined?(:@@db_backup_paths)
      extra_exclude_path = @@db_backup_paths.select{|p| p.start_with?(home_dir)}
    end
    if self.path.start_with?(home_dir)
      extra_exclude_path << self.path if extra_exclude_path.exclude?(self.path)
    end
    if extra_exclude_path.count != 0
      tmp = []
      extra_exclude_path.each do |s|
        if tmp.count == 0
          tmp << s
        else
          if tmp.exclude?(s)
            s2 = File.dirname(s)
            idx = tmp.index{|ss| File.dirname(ss) == s2}
            if idx
              tmp[idx] = s2
            else
              tmp << s
            end
          end
        end
      end
      backup_text += "\t#{tmp.map{|p| "exclude=#{p}"}.join(',')}"
    end
    backup_text
  end
  def snapshot_root
    "snapshot_root\t#{self.path}"
  end
  def self.read_from_crontab_line(crontab_line)
    if crontab_line.include?('rsnapshot')
      tmp = crontab_line.split(/\s+[^\s]*rsnapshot\s+/) # ex: ["15 4 * * *", "daily"], ["15 4 * * *", "-c /etc/rsnapshot.conf daily"]
      if tmp.count == 2
        crontab_time = tmp[0].split(/\s+/)
        period_text = tmp[1]
        rsnapshot_conf_path = DefaultConf
        if period_text.start_with?('-c')
          tmp2 = period_text.split(/\s+/)
          if tmp2.count == 3
            rsnapshot_conf_path = tmp2[1]
            period_text = tmp2[2]
          else
            period_text = tmp2.last
          end
        end
        if PeriodsTypes.include?(period_text)
          period = PeriodsTypes.index(period_text)
        else
          period = 0
        end
        time = "#{crontab_time[1].rjust(2, "0")}:#{crontab_time[0].rjust(2, "0")}"
        return [time, rsnapshot_conf_path, period]
      else
        return nil
      end
    else
      return nil
    end
  end
  def self.clean_crontab_setting(crontab_lines_str)
    crontab_lines = crontab_lines_str.split("\n")
    crontab_lines_str = crontab_lines.map do |crontab_line|
      if crontab_line.include?('rsnapshot')
        nil
      else
        crontab_line
      end
    end.compact.join("\n")
  end
  def self.init_class_variables(site_server)
    @@db_backup_paths = SiteServerDbBackup.where(:site_server_id=> site_server.id, :disable=>false).pluck(:path)
  end
  def self.write_crontab_setting(site_server, crontab_lines_str)
    new_crontab_lines = self.where(:site_server_id=> site_server.id, :disable=>false).flat_map{|file_backup| file_backup.to_crontab_script.split("\n")}
    crontab_lines = crontab_lines_str.split("\n")
    exists_indices = new_crontab_lines.map do |new_crontab_line|
      match_reg = ::Regexp.new(new_crontab_line.gsub(/\s+/, '\s+'))
      crontab_lines.index{|l| l.match(match_reg)}
    end
    crontab_lines = crontab_lines.map.with_index do |crontab_line, i|
      if crontab_line.include?('rsnapshot') && exists_indices.exclude?(i)
        nil
      else
        crontab_line
      end
    end
    exists_indices.each_with_index do |idx, i|
      if idx.nil?
        crontab_lines << new_crontab_lines[i]
      end
    end
    crontab_lines_str = crontab_lines.compact.join("\n")
  end
end