require 'active_support'
require 'active_support/core_ext'
require 'erb'
require 'camelizable'
require 'yaml'

class String
  include Camelizable
  def make_field
    self.gsub(/\./, "_")
  end
  def make_name
    if self == "type"
      "r#type"
    else
      self
    end
  end
end

# only object
def make_body(body, results, array_name=nil)
  body[:properties].each do |it|
    if it[:type] == "object"
      make_body(it, results)
    elsif it[:type] == "array" && it[:items][:type] == "object"
        make_body(it[:items], results, it[:name].singularize)
    elsif it[:type] == "enum_single"
      results << make_expantions(it)
    end
  end
  name = array_name || body[:name] || "body"
  properties = body[:properties]
  erb = ERB.new(File.read("body.erb"))
  results << erb.result(binding)
end

def make_expantions(it)
  @expantion_flag = true
  class_name = it[:name].make_field.ucc
  src = it[:value]
  ary = src.split(", ")
  all_flag = it[:type] == "enum"
  use_flag = false
  methods = []
  erb = ERB.new(File.read("expantions.erb"))
  erb.result(binding)
end

def execute(path)
  @expantion_flag = false
  m = /api\/(.+)\.yaml/.match(path)
  name = m[1]

  yml = YAML.load_file(path).deep_symbolize_keys
  paths = (yml[:paths] || []).map{|it| it[:required] = "true"; it }
  queries = yml[:queries] || []
  form = yml[:form] || []
  enum_flag = queries.filter{|it| it[:type] == "enum"}.present?
  @date_flag = queries.filter{|it| it[:type] == "date" }.present?

  self_required = (queries + form.filter{|it| it[:alter].blank?}).filter{|it| it[:required] } + paths
  required_queries = queries.filter{|it| it[:required] }
  non_required_queries = queries.filter{|it| !it[:required] }

  fields = queries.filter{|it| /\.fields$/ =~ it[:name]}

  auth = if yml[:auth] == "basic"
    {
      method: ".basic_auth(self.api_key_code, Some(self.api_secret_code))",
      keys: ["api_key_code", "api_secret_code"],
    }
  else
    {
      method: ".bearer_auth(self.bearer_code)",
      keys: ["bearer_code"],
    }
  end

  expantions = (queries + form).filter{|it| (it[:type] == "enum" || it[:type] == "enum_single") && !(/\.fields$/ =~ it[:name]) }.map do |it|
    make_expantions(it)
  end
  bodies = []
  make_body(yml[:body], bodies) if yml[:body].present?

  response_list = []
  responses, refs, map = make_response("response", yml.dig(:response, :properties), false)
  response_list << responses if responses.present?
  if map.present?
    map.each_pair do |key, value|
      responses, _, _ = make_response(key.to_s, value.dig(:properties), false)
      response_list << responses
    end
  end

  serde_flag = @expantion_flag || bodies.present? || response_list.present?
  api_struct = ERB.new(File.read("api_struct.erb")).result(binding)
  api_new = ERB.new(File.read("api_new.erb")).result(binding).gsub(/^/, "    ")
  setter = ERB.new(File.read("setter.erb")).result(binding).gsub(/^/, "    ")
  parameters = ERB.new(File.read("parameters.erb")).result(binding).gsub(/^/, "        ")
  erb = ERB.new(File.read("api.erb"))
  File.write("../src/api/#{name}.rs", erb.result(binding))
end

def execute_expantions(path)
  m = /fields\/(.+)\.yaml/.match(path)
  name = m[1]
  yml = YAML.load_file(path).deep_symbolize_keys

  class_name = name.make_field.ucc + "Fields"
  src = yml[:value]
  ary = src.split(", ")
  all_flag = true
  use_flag = true
  methods = yml[:methods] || []
  erb = ERB.new(File.read("expantions.erb"))
  File.write("../src/fields/#{name}_fields.rs", erb.result(binding))
end

def calc_refs(name, properties, refs)
  properties.each_pair do |key, value|
    if value[:type] == "object"
      if value[:ref].present? && value[:ref] != name.to_s
        refs << value[:ref]
      elsif value[:properties].present?
        calc_refs(key, value[:properties], refs)
      end
    elsif value[:type] == "array" && value[:items][:type] == "object" && value[:items][:ref] != name.to_s
      refs << value[:items][:ref]
    end
  end
end

def make_response(name, properties, independence_flag)
  return ["", [], {}] if properties.blank?
  refs = []
  @enums = {}
  @inner_map = {}
  calc_refs(name, properties, refs)
  refs.uniq!
  class_name = name.make_field.ucc
  erb = ERB.new(File.read("responses.erb"))
  [erb.result(binding), refs, @inner_map]
end

def execute_responses(path)
  @date_flag = false
  m = /responses\/(.+)\.yaml/.match(path)
  name = m[1]
  yml = YAML.load_file(path).deep_symbolize_keys
  properties = yml[:response][:properties]
  independence_flag = true
  res, refs, map = make_response(name, properties, independence_flag)
  map.deep_dup.each_pair do |key, value|
    responses, _, _ = make_response(key.to_s, value.dig(:properties), false)
    res = res + "\n" + responses
  end

  File.write("../src/responses/#{name}.rs", res.gsub(/USE_DATE/, @date_flag ? "\nuse chrono::prelude::*;" : ""))
end

Dir.glob('api/*.yaml').each do |path|
  execute(path)
end

Dir.glob('fields/*.yaml').each do |path|
  execute_expantions(path)
end

Dir.glob('responses/*.yaml').each do |path|
  execute_responses(path)
end