# -*- coding: utf-8 -*-
require "magellan/cli/resources"
require 'cgi'

module Magellan
  module Cli
    module Resources

      class Stage < Base
        include Deletable

        self.resource_key = "stage~title"
        self.resource_dependency = {"project" => Project.parameter_name}
        self.hidden_fields = %w[nebula_id created_at updated_at].map(&:freeze).freeze
        self.field_associations = {"project_id" => {name: "project", class: "Project"} }

        VERSION_RESOURCE_KEY = "stage~version".freeze
        VERSION_PARAMETER_NAME = "stage_version".freeze

        desc "create NAME [-t development|staging|production]", I18n.t(:create, scope: [:resources, :stage, :cmd], resource_name: resource_name)
        option :t, type: :string, default: "development", desc: I18n.t(:type, scope: [:resources, :stage, :cmd_create])
        def create(name)
          type = options["t"]
          unless %w{ development staging production }.include?(type)
            raise "Unknown Stage Type #{type}"
          end
          proj = load_selection!(Project)
          params = {
            parameter_name => {
              "project_id" => proj["id"],
              "name" => name,
              "stage_type" => type,
            }
          }
          post_json("/admin/#{resource_key}/new.json", params)
          select(name)
        end

        no_commands do
          def build_name_query(name)
            build_query("name" => name).update(default_query)
          end
        end

        desc "select NAME", I18n.t(:select, scope: [:resources, :common, :cmd], res_name: resource_name)
        def select(name)
          if selected = load_selections[parameter_name]
            deselect unless selected["name"] == name
          end

          q = build_name_query(name)
          r = update_first_result(parameter_name, name, "/admin/stage~title.json", q)

          # # current
          # q = build_query("title" => r["id"], "phase" => 2) # 2: current
          # update_first_result(VERSION_PARAMETER_NAME, "phase=2", "/admin/stage~version.json", q, %w[id])

          # # workspace
          q = build_query("title" => r["id"], "phase" => 1) # 1: workspace
          update_first_result(VERSION_PARAMETER_NAME, "phase=1", "/admin/stage~version.json", q, %w[id])
        end

        def self.deselect(selections)
          selections.delete(parameter_name)
          selections.delete(VERSION_PARAMETER_NAME)
          deselect_dependants(selections)
        end

        desc "planning", I18n.t(:planning, scope: [:resources, :stage, :cmd])
        def planning
          switch_version(1)
        end

        desc "current", I18n.t(:current, scope: [:resources, :stage, :cmd])
        def current
          switch_version(2)
        end

        no_commands do
          def switch_version(phase)
            s = load_selection!(self.class)
            q = build_query("title" => s["id"], "phase" => phase) # 1: workspace, 2: current, 3: used
            update_first_result(VERSION_PARAMETER_NAME, "phase=#{phase}", "/admin/stage~version.json", q, %w[id])
          end
        end

        desc "prepare", I18n.t(:prepare, scope: [:resources, :stage, :cmd], containers_name: Container.resource_name.pluralize)
        def prepare
          s = load_selection!(self.class)
          id = s["id"]
          r = post_json("/admin/stage~title/#{id}/simple_method_call.json", {method_name: "prepare_containers"})
          Container.new.show_list(r["result"])
        end

        desc "repair", I18n.t(:repair, scope: [:resources, :stage, :cmd], resource_name: resource_name)
        def repair
          r = call_repair
          puts r["success"] ? "\e[32msucceeded to repair stage\e[0m" : "\e[31mfailed to repair stage\e[0m"
        end

        no_commands do
          def call_repair
            s = load_selection!(self.class)
            id = s["id"]
            post_json("/admin/stage~title/#{id}/simple_method_call.json", {method_name: "repair"})
          end
        end

        desc "update ATTRIBUTES", I18n.t(:update, scope: [:resources, :common, :cmd], resource_name: resource_name)
        def update(attrs)
          s = load_selection!(self.class)
          attrs = JSON.parse(File.readable?(attrs) ? File.read(attrs) : attrs)
          put_json("/admin/stage~title/#{s['id']}/edit.json", {"stage_title" => attrs})
        end

        desc "release_now", I18n.t(:release_now, scope: [:resources, :stage, :cmd])
        option :A, type: :boolean, default: false, desc: I18n.t(:async, scope: [:resources, :stage, :cmd_release_now])
        option :i, type: :numeric, default: 10, desc: I18n.t(:interval, scope: [:resources, :stage, :cmd_release_now])
        option :t, type: :numeric, default: 600, desc: I18n.t(:timeout, scope: [:resources, :stage, :cmd_release_now])
        def release_now
          spacer = "\r" << (" " * 20)
          stage = load_selection!(self.class)
          print "\rrelease starting"
          id = stage["id"]
          res0 = post_json("/admin/stage~title/#{id}/simple_method_call.json", {method_name: "release_now"})
          res1 = res0["result"]
          if res1
            print spacer
            print "\r#{res1['status']}"
          else
            print spacer
            puts "\e[31m#{res0.inspect}\e[0m"
            raise res0["message"]
          end

          return res1 if options["A"]
          res2 = get_json("/admin/release~operation.json", build_query("release_job" => res1["id"]))
          ope = res2.first
          ope_id = ope["id"]
          i = options["i"]
          Timeout.timeout(options["t"]) do
            loop do
              sleep(i)
              res3 = get_json("/admin/release~operation/#{ope_id}.json", default_query)
              st = res3["status"]
              unless res1["status"] == st
                case st
                when "executing" then
                  res4 = get_json("/admin/release~transaction.json", build_query("release_operation" => ope_id))
                  total = res4.length
                  complete = res4.select{ |r| r["status"] == "completed" }.length
                  print spacer
                  print "\rProgress: %2d /%2d" % [complete, total]
                when "completed"
                  print spacer
                  puts "\r#{st}"
                  reload
                  return
                when "aborted" then
                  print spacer
                  puts "\rrelease #{st}"
                  puts "now repairing stage automatically..."
                  r = call_repair
                  puts r["success"] ? "succeeded to repair stage. try `stage release_now` again after fix" : "\e[31mfailed to repair stage\e[0m"
                  raise Magellan::Cli::Error, "release #{st}"
                end
              end
            end
          end
        end

        desc "logs", I18n.t(:logs, scope: [:resources, :stage, :cmd], workers_name: Worker.resource_name.pluralize)
        def logs
          s = load_selection!(self.class)
          id = s["id"]
          obj = get_json("/admin/stage~title/#{id}/logs.json")
          if obj["value"]
            obj["value"].each do |log|
              puts "#{Time.at(log["time"].to_f).strftime("%Y-%m-%d %H:%M:%S")}:#{log["version"]}:#{log["container"]}: #{log["message"]}"
            end
          end
        end

        desc "set_container_num NUM", I18n.t(:set_container_num, scope: [:resources, :stage, :cmd], containers_name: Container.resource_name.pluralize, image_name: Image.resource_name)
        def set_container_num(num)
          s = load_selection!(self.class)
          v = load_selection!(VERSION_PARAMETER_NAME)
          i = load_selection!(Image)
          post_json("/admin/stage~version/#{v["id"]}/set_container_num.json", { container_num: num, container_image_id: i["id"] })
        end

        desc "reload", I18n.t(:reload, scope: [:resources, :stage, :cmd])
        def reload
          s = load_selection!(self.class)
          select(s["name"])
          [Worker, Image, Container].each do |klass|
            s = (load_selections || {})[klass.parameter_name]
            next unless s
            name = s["name"]
            next unless name
            klass.new.select(name)
          end
        end
      end

    end
  end
end