require 'geo_ruby/geojson'
require 'phone'
require 'sequel'

ENV['DATABASE_URL'] ||= "postgres://localhost/citygram_#{Citygram::App.environment}"
DB = Sequel.connect(ENV['DATABASE_URL'], max_connections: ENV.fetch('MAX_CONNECTIONS', 20))

Sequel.default_timezone = :utc

# no mass-assignable columns by default
Sequel::Model.set_allowed_columns(*[])

# use first!, create!, save! to raise
Sequel::Model.raise_on_save_failure = false

# sequel's standard pagination
DB.extension :pagination

DB.extension :pg_json, :pg_array

# common model plugins
Sequel::Model.plugin :attributes_helpers
Sequel::Model.plugin :json_serializer
Sequel::Model.plugin :save_helpers
Sequel::Model.plugin :serialization
Sequel::Model.plugin :timestamps, update_on_create: true
Sequel::Model.plugin :touch
Sequel::Model.plugin :validation_helpers

Sequel::Plugins::Serialization.register_format(:pg_array,
  ->(v){ Sequel.pg_array(v) },
  ->(v){
    case v
    when Sequel::Postgres::PGArray
      v.to_a
    when String
      Sequel::Postgres::PGArray::Parser.new(v).parse
    else
      raise Sequel::InvalidValue, "invalid value for array: #{v.inspect}"
    end
  }
)

# roundtrip a `Hash` or `Array` through a native postgres json column
Sequel::Plugins::Serialization.register_format(:pg_json,
  ->(v){ Sequel.pg_json(v) },
  ->(v){
    # TODO: does sequel expose an interface for this case handling?
    case v
    when Sequel::Postgres::JSONHash
      v.to_h
    when Sequel::Postgres::JSONArray
      v.to_a
    when String
      Sequel.parse_json(v)
    else
      raise Sequel::InvalidValue, "invalid value for json: #{v.inspect}"
    end
  }
)

# round trip a geojson geometry through a postgis geometry column
Sequel::Plugins::Serialization.register_format(:geojson,
  # transform a geojson geometry into extended well-known text format
  ->(v){ GeoRuby::GeojsonParser.new.parse(v).as_ewkt },
  # transform extended well-known binary into a geojson geometry
  ->(v){ GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(v).to_json }
)

# set default to US for now
Phoner::Phone.default_country_code = '1'

# normalize phone numbers to E.164
Sequel::Plugins::Serialization.register_format(:phone,
  ->(v){ Phoner::Phone.parse(v).to_s },
  ->(v){ v } # identity
)

module Citygram
  module Models
  end
end

require 'app/models/city'
require 'app/models/event'
require 'app/models/outage'
require 'app/models/publisher'
require 'app/models/sms_credentials'
require 'app/models/subscription'

# access model class constants without qualifying namespace
include Citygram::Models
