# frozen_string_literal: true

require "cases/helper"
require "support/schema_dumping_helper"

class PostgresqlIntervalTest < ActiveRecord::PostgreSQLTestCase
  include SchemaDumpingHelper

  class IntervalDataType < ActiveRecord::Base
    attribute :maximum_term, :interval
    attribute :minimum_term, :interval, precision: 3
    attribute :default_term, :interval
    attribute :all_terms,    :interval, array: true
    attribute :legacy_term,  :string
  end

  class DeprecatedIntervalDataType < ActiveRecord::Base; end

  def setup
    @connection = ActiveRecord::Base.connection
    @connection.transaction do
      @connection.create_table("interval_data_types") do |t|
        t.interval "maximum_term"
        t.interval "minimum_term", precision: 3
        t.interval "default_term", default: "P3Y"
        t.interval "all_terms", array: true
        t.interval "legacy_term"
      end
      @connection.create_table("deprecated_interval_data_types") do |t|
        t.interval "duration"
      end
    end
    @column_max = IntervalDataType.columns_hash["maximum_term"]
    @column_min = IntervalDataType.columns_hash["minimum_term"]
    assert(@column_max.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLColumn))
    assert(@column_min.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLColumn))
    assert_nil @column_max.precision
    assert_equal 3,   @column_min.precision
  end

  teardown do
    @connection.execute "DROP TABLE IF EXISTS interval_data_types"
    @connection.execute "DROP TABLE IF EXISTS deprecated_interval_data_types"
  end

  def test_column
    assert_equal :interval,     @column_max.type
    assert_equal :interval,     @column_min.type
    assert_equal "interval",    @column_max.sql_type
    assert_equal "interval(3)", @column_min.sql_type
  end

  def test_interval_type
    IntervalDataType.create!(
      maximum_term: 6.year + 5.month + 4.days + 3.hours + 2.minutes + 1.seconds,
      minimum_term: 1.year + 2.month + 3.days + 4.hours + 5.minutes + (6.234567).seconds,
      all_terms:    [1.month, 1.year, 1.hour],
      legacy_term:  "33 years",
    )
    i = IntervalDataType.last!
    assert_equal "P6Y5M4DT3H2M1S",     i.maximum_term.iso8601
    assert_equal "P1Y2M3DT4H5M6.235S", i.minimum_term.iso8601
    assert_equal "P3Y",                i.default_term.iso8601
    assert_equal %w[ P1M P1Y PT1H ],   i.all_terms.map(&:iso8601)
    assert_equal "P33Y",               i.legacy_term
  end

  def test_interval_type_cast_from_invalid_string
    i = IntervalDataType.create!(maximum_term: "1 year 2 minutes")
    i.reload
    assert_nil i.maximum_term
  end

  def test_interval_type_cast_from_numeric
    i = IntervalDataType.create!(minimum_term: 36000)
    i.reload
    assert_equal "PT10H",  i.minimum_term.iso8601
  end

  def test_interval_type_cast_string_and_numeric_from_user
    i = IntervalDataType.new(maximum_term: "P1YT2M", minimum_term: "PT10H", legacy_term: "P1DT1H")
    assert i.maximum_term.is_a?(ActiveSupport::Duration)
    assert i.legacy_term.is_a?(String)
    assert_equal "P1YT2M", i.maximum_term.iso8601
    assert_equal "PT10H",  i.minimum_term.iso8601
    assert_equal "P1DT1H", i.legacy_term
  end

  def test_deprecated_legacy_type
    assert_deprecated do
      DeprecatedIntervalDataType.new
    end
  end

  def test_schema_dump_with_default_value
    output = dump_table_schema "interval_data_types"
    assert_match %r{t\.interval "default_term", default: "P3Y"}, output
  end
end
