_           = require 'underscore'
dotty       = require 'dotty'
fs          = require 'fs'
Readable    = require 'readable-stream'
sinon       = require 'sinon'
Understream = require 'understream'
_.mixin require('underscore.string').exports()
_.mixin require('underscore.deep')

# This is still an unstable interface.
module.exports = (api_key, data_dir) ->
  throw new Error "Must provide api_key" unless api_key?
  throw new Error "Must provide data_dir" unless data_dir?

  clever = require("./index") api_key, "localhost"

  trequire = (fp) ->
    val = []
    if fs.existsSync("#{fp}.coffee") or fs.existsSync("#{fp}.js") or fs.existsSync("#{fp}.json")
      try
        val = require(fp)
      catch err
        throw new Error "Error loading file at #{fp}: #{err}"
    val

  # clever.db[resource] are arrays of resources
  clever.db =
    districts: trequire("#{data_dir}/districts")
    districtproperties: trequire("#{data_dir}/districtproperties")
    students: trequire("#{data_dir}/students")
    studentproperties: trequire("#{data_dir}/studentproperties")
    teachers: trequire("#{data_dir}/teachers")
    teacherproperties: trequire("#{data_dir}/teacherproperties")
    schools: trequire("#{data_dir}/schools")
    schoolproperties: trequire("#{data_dir}/schoolproperties")

  # clever.map[resources] are maps from ids to resources (for faster lookup)
  # This doesn't just replace clever.db for compatibility reasons..for now..
  clever.refresh_map = ->
    map_id_to_val = (db, resource, id_key) ->
      map = {}
      map[obj[id_key]] = obj for i, obj of db[resource]
      map
    clever.map =
      districts: map_id_to_val clever.db, 'districts', 'id'
      districtproperties: map_id_to_val clever.db, 'districtproperties', 'district'
      students: map_id_to_val clever.db, 'students', 'id'
      studentproperties: map_id_to_val clever.db, 'studentproperties', 'student'
      teachers: map_id_to_val clever.db, 'teachers', 'id'
      teacherproperties: map_id_to_val clever.db, 'teacherproperties', 'teacher'
      schools: map_id_to_val clever.db, 'schools', 'id'
      schoolproperties: map_id_to_val clever.db, 'schoolproperties', 'school'
  clever.refresh_map()

  sandbox = sinon.sandbox.create()

  apply_query = (undersomething, conditions, resource) ->
    return undersomething.filter (obj) ->
      for key, val of conditions
        return false unless obj[key] is val
      return true
    .map (obj) ->
      if obj._shadow? then  _.extend({}, obj, obj._shadow) else obj
    .map (raw_json) ->
      Klass = clever[_(resource).chain().capitalize().rtrim('s').value()]
      return new Klass _.deepClone raw_json

  sandbox.stub clever.Query.prototype, 'exec', (cb) ->
    resource = _.strRightBack(@_url, '/')
    s = apply_query _(clever.db[resource]).chain(), @_conditions, resource
    return setImmediate cb, null, s.value().length if @_options.count
    setImmediate cb, null, s.value()

  sandbox.stub clever.Query.prototype, 'stream', ->
    resource = _.strRightBack(@_url, '/')
    s = apply_query new Understream(clever.db[resource]), @_conditions, resource
    return s.stream()

  sandbox.stub clever.Resource.prototype, 'properties', (obj, cb) ->
    if arguments.length is 1
      cb = obj
      obj = undefined
    else if arguments.length isnt 2
      throw new Error("expected 1 or 2 arguments to properties")
    resource = @constructor.name.toLowerCase() + 's'
    resource_singular = _(resource).rtrim('s')
    id = @_properties.id
    clever_resource = clever.map[resource][id]
    return setImmediate cb(new Error("404")) unless clever_resource?
    resource_property = "#{resource_singular}properties"
    prop_obj = clever.map[resource_property][id]
    if not prop_obj
      prop_obj = _.object [[resource_singular, id], ['data', {}]]
      clever.db[resource_property].push prop_obj
      clever.map[resource_property][id] = prop_obj
    if obj
      dotty.put prop_obj.data, k, v for k, v of obj
      if obj.google_apps?.username?
        clever_resource._shadow ?= {}
        clever_resource._shadow.email = obj.google_apps.username
    setImmediate cb, null, _.deepClone prop_obj.data

  clever
