# typed: strict
require "active_support/concern"

module HasColorLogger
  extend T::Sig
  extend T::Helpers
  requires_ancestor { Kernel }

  sig { params(sink: T.any(IO, StringIO)).returns(Module) }
  def self.[](sink)
    Module.new do
      extend ActiveSupport::Concern
      included do
        define_method(:logger) do
          @logger ||= T.let(ColorLogger.make(sink), T.nilable(ColorLogger))
        end

        define_singleton_method(:logger) do
          @logger ||= T.let(ColorLogger.make(sink), T.nilable(ColorLogger))
        end
      end
    end
  end

  sig { params(tag_name: String, tag_value: T.untyped).returns(String) }
  def make_tag(tag_name, tag_value)
    self.class.make_tag(tag_name, tag_value)
  end

  sig { params(tags: T.any(String, T::Array[String])).returns(String) }
  def format_tags(*tags)
    self.class.format_tags(*T.unsafe([tags].flatten))
  end

  sig { params(tags: T::Array[String]).returns(String) }
  def format_tags_arr(tags)
    self.class.format_tags_arr(tags)
  end

  sig do
    params(arg: T.untyped, name: T.nilable(String)).returns(T::Array[String])
  end
  def make_arg_tag(arg, name: nil)
    self.class.make_arg_tag(arg, name:)
  end

  module ClassMethods
    extend T::Sig

    sig { params(tag_name: String, tag_value: T.untyped).returns(String) }
    def make_tag(tag_name, tag_value)
      tag_value_str = tag_value ? tag_value.to_s.bold : "(nil)".italic
      "#{tag_name}: #{tag_value_str}"
    end

    sig { params(tags: String).returns(String) }
    def format_tags(*tags)
      format_tags_arr(tags)
    end

    sig { params(tags: T::Array[String]).returns(String) }
    def format_tags_arr(tags)
      tags.map { |tag| "[#{tag}]" }.join(" ")
    end

    sig do
      params(arg: T.untyped, name: T.nilable(String)).returns(T::Array[String])
    end
    def make_arg_tag(arg, name: nil)
      tags = []
      return tags if arg.nil?

      case arg
      when Domain::User
        name ||= "user"
        tags << make_tag("#{name}.param", arg.to_param)
      when Domain::Post
        name ||= "post"
        tags << make_tag("#{name}.param", arg.to_param)
      when Domain::PostFile
        name ||= "file"
        tags << make_tag("#{name}.id", arg.id)
        tags << make_tag("#{name}.state", arg.state)
      when Domain::UserAvatar
        name ||= "avatar"
        tags << make_tag("#{name}.id", arg.id)
        tags << make_tag("#{name}.state", arg.state)
      when Domain::PostGroup
        name ||= "pool"
        tags << make_tag("#{name}.id", arg.id)
      when HttpLogEntry
        name ||= "hle"
        tags << make_tag("#{name}.id", arg.id)
        tags << make_tag("#{name}.code", arg.status_code)
      when HasTimestampsWithDueAt::TimestampScanInfo
        name ||= "scan_info"
        at = arg.at
        tags << make_tag(
          "#{name}.at",
          at ? DateHelper.time_ago_in_words(at) : "never",
        )
        tags << make_tag("#{name}.due", arg.due?)
      else
        tags << make_tag("unknown", arg.class.name)
      end

      tags
    end
  end
  mixes_in_class_methods(ClassMethods)

  # by default, write to stdout
  extend ActiveSupport::Concern
  included { include HasColorLogger[$stdout] }
end
