require_relative "../shared_examples/associations"
require_relative "../shared_examples/commands"
require_relative "../shared_examples/connection"
require_relative "../shared_examples/default_fields"
require_relative "../shared_examples/including"
require_relative "../shared_examples/limiting"
require_relative "../shared_examples/logging"
require_relative "../shared_examples/ordering"
require_relative "../shared_examples/qualifications"
require_relative "../shared_examples/queries"
require_relative "../shared_examples/query_default"
require_relative "../shared_examples/results"
require_relative "../shared_examples/types"

require_relative "./shared_examples/literal"
require_relative "./shared_examples/migrations"
require_relative "./shared_examples/operations"
require_relative "./shared_examples/raw"
require_relative "./shared_examples/table"
require_relative "./shared_examples/transactions"
require_relative "./shared_examples/types"

RSpec.describe "mysql source", mysql: true do
  it_behaves_like :source_associations
  it_behaves_like :source_commands
  it_behaves_like :source_connection
  it_behaves_like :source_default_fields
  it_behaves_like :source_including
  it_behaves_like :source_limiting
  it_behaves_like :source_logging
  it_behaves_like :source_ordering
  it_behaves_like :source_qualifications
  it_behaves_like :source_queries
  it_behaves_like :source_query_default
  it_behaves_like :source_results
  it_behaves_like :source_types

  it_behaves_like :source_sql_literal
  it_behaves_like :source_sql_migrations, adapter: :mysql2
  it_behaves_like :source_sql_operations
  it_behaves_like :source_sql_raw
  it_behaves_like :source_sql_table
  it_behaves_like :source_sql_transactions
  it_behaves_like :source_sql_types

  let :connection_type do
    :sql
  end

  let :connection_string do
    ENV["DATABASE_URL__MYSQL"]
  end

  describe "default primary id" do
    before do
      local_connection_type, local_connection_string = connection_type, connection_string

      Pakyow.after "configure" do
        config.data.connections.public_send(local_connection_type)[:default] = local_connection_string
      end
    end

    include_context "app"

    let :app_def do
      Proc.new do
        source :posts, primary_id: false, timestamps: false do
          primary_id
        end
      end
    end

    let :column do
      schema(:posts)[0][1]
    end

    it "is a primary key" do
      expect(column[:primary_key]).to eq(true)
    end

    it "auto increments" do
      expect(column[:auto_increment]).to eq(true)
    end

    it "does not allow null" do
      expect(column[:allow_null]).to eq(false)
    end

    it "has no default" do
      expect(column[:default]).to eq(nil)
    end

    it "is a bignum integer" do
      expect(column[:db_type]).to eq("bigint")
      expect(column[:type]).to eq(:integer)
    end

    context "primary key is a custom type" do
      it "is of the correct type"
    end
  end

  describe "default foreign key" do
    before do
      local_connection_type, local_connection_string = connection_type, connection_string

      Pakyow.after "configure" do
        config.data.connections.public_send(local_connection_type)[:default] = local_connection_string
      end
    end

    include_context "app"

    let :app_def do
      Proc.new do
        source :posts, primary_id: false, timestamps: false do
          primary_id
          has_many :comments
        end

        source :comments, primary_id: false, timestamps: false do
          primary_id
        end
      end
    end

    let :column do
      data.comments.source.class.container.connection.adapter.connection.schema(:comments)[1][1]
    end

    it "is not a primary key" do
      expect(column[:primary_key]).to eq(false)
    end

    it "does not auto increment" do
      expect(column[:auto_increment]).to eq(nil)
    end

    it "allows null" do
      expect(column[:allow_null]).to eq(true)
    end

    it "has no default" do
      expect(column[:default]).to eq(nil)
    end

    it "is a bignum integer" do
      expect(column[:db_type]).to eq("bigint")
      expect(column[:type]).to eq(:integer)
    end

    context "primary key for the foreign source is a custom type" do
      it "matches the type"
    end
  end

  describe "column types" do
    before do
      local_connection_type, local_connection_string = connection_type, connection_string

      Pakyow.after "configure" do
        config.data.connections.public_send(local_connection_type)[:default] = local_connection_string
      end
    end

    include_context "app"

    let :column do
      schema(:posts)[0][1]
    end

    let :app_def do
      context = self

      Proc.new do
        source :posts, primary_id: false, timestamps: false do
          attribute :test, context.type
        end
      end
    end

    describe "boolean" do
      let :type do
        :boolean
      end

      it "is the correct db type" do
        expect(column[:db_type]).to eq("tinyint(1)")
      end

      it "is the correct type" do
        expect(column[:type]).to eq(:boolean)
      end
    end

    describe "date" do
      let :type do
        :date
      end

      it "is the correct db type" do
        expect(column[:db_type]).to eq("date")
      end
    end

    describe "datetime" do
      let :type do
        :datetime
      end

      it "is the correct db type" do
        expect(column[:db_type]).to eq("datetime")
      end
    end

    describe "decimal" do
      let :type do
        :decimal
      end

      it "is the correct db type" do
        expect(column[:db_type]).to eq("decimal(10,2)")
      end
    end

    describe "float" do
      let :type do
        :float
      end

      it "is the correct db type" do
        expect(column[:db_type]).to eq("float")
      end
    end

    describe "integer" do
      let :type do
        :integer
      end

      it "is the correct db type" do
        expect(column[:db_type]).to eq("int")
      end
    end

    describe "string" do
      let :type do
        :string
      end

      it "is the correct db type" do
        expect(column[:db_type]).to eq("varchar(255)")
      end
    end

    describe "time" do
      let :type do
        :time
      end

      it "is the correct db type" do
        expect(column[:db_type]).to eq("datetime")
      end
    end

    describe "file" do
      let :type do
        :file
      end

      it "is the correct db type" do
        expect(column[:db_type]).to eq("blob")
      end
    end

    describe "text" do
      let :type do
        :text
      end

      it "is the correct db type" do
        expect(column[:db_type]).to eq("text")
      end
    end

    describe "bignum" do
      let :type do
        :bignum
      end

      it "is the correct db type" do
        expect(column[:db_type]).to eq("bigint")
      end
    end
  end
end
