require_relative 'tree_docs'

class ArchivesSpaceService < Sinatra::Base

  Endpoint.post('/repositories/:repo_id/resources')
    .description("Create a Resource")
    .params(["resource", JSONModel(:resource), "The record to create", :body => true],
            ["repo_id", :repo_id])
    .permissions([:update_resource_record])
    .returns([200, :created],
             [400, :error]) \
  do
    handle_create(Resource, params[:resource])
  end


  Endpoint.get('/repositories/:repo_id/resources/:id')
    .description("Get a Resource")
    .params(["id", :id],
            ["repo_id", :repo_id],
            ["resolve", :resolve])
    .permissions([:view_repository])
    .returns([200, "(:resource)"]) \
  do
    json = Resource.to_jsonmodel(params[:id])
    json_response(resolve_references(json, params[:resolve]))
  end


  Endpoint.get('/repositories/:repo_id/resources/:id/ordered_records')
    .description("Get the list of URIs of this published resource and all published archival objects contained within." +
                 "Ordered by tree order (i.e. if you fully expanded the record tree and read from top to bottom)")
    .params(["id", :id],
            ["repo_id", :repo_id])
    .permissions([:view_repository])
    .returns([200, "JSONModel(:resource_ordered_records)"]) \
  do
    resource = Resource.get_or_die(params[:id])

    json_response(JSONModel(:resource_ordered_records).from_hash({:uris => resource.ordered_records}, raise_errors = true, trusted = true))
  end


  Endpoint.get('/repositories/:repo_id/resources/:id/top_containers')
    .description("Get Top Containers linked to a published resource and published archival ojbects contained within.")
    .params(["id", :id],
            ["repo_id", :repo_id],
            ["resolve", :resolve])
    .permissions([:view_repository])
    .returns([200, "a list of linked top containers"],
             [404, "Not found"]) \
  do
    resource = Resource.get_or_die(params[:id])
    top_container = []
    records = resource.ordered_records.map {|record| record = record['ref']}

    records.each do |record|
      ref = JSONModel.parse_reference(record)
      case ref[:type]
      when "resource"
        obj = Resource.to_jsonmodel(ref[:id])
      else
        obj = ArchivalObject.to_jsonmodel(ref[:id])
      end
      top_container.push(obj[:instances].map {|instance|
                    instance['instance_type'] == 'digital_object' ? nil :
                    instance['sub_container']['top_container']
                  }.compact)

    end

    json_response(top_container.uniq.flatten)

  end


  Endpoint.post('/repositories/:repo_id/resources/:id')
    .description("Update a Resource")
    .params(["id", :id],
            ["resource", JSONModel(:resource), "The updated record", :body => true],
            ["repo_id", :repo_id])
    .permissions([:update_resource_record])
    .returns([200, :updated],
             [400, :error]) \
  do
    handle_update(Resource, params[:id], params[:resource])
  end


  Endpoint.get('/repositories/:repo_id/resources')
    .description("Get a list of Resources for a Repository")
    .params(["repo_id", :repo_id])
    .paginated(true)
    .permissions([:view_repository])
    .returns([200, "[(:resource)]"]) \
  do
    handle_listing(Resource, params)
  end


  Endpoint.delete('/repositories/:repo_id/resources/:id')
    .description("Delete a Resource")
    .params(["id", :id],
            ["repo_id", :repo_id])
    .permissions([:delete_archival_record])
    .returns([200, :deleted]) \
  do
    handle_delete(Resource, params[:id])
  end


  Endpoint.post('/repositories/:repo_id/resources/:id/publish')
  .description("Publish a resource and all its sub-records and components")
  .params(["id", :id],
          ["repo_id", :repo_id])
  .permissions([:update_resource_record])
  .no_data(true)
  .returns([200, :updated],
           [400, :error]) \
  do
    resource = Resource.get_or_die(params[:id])
    resource.publish!

    updated_response(resource)
  end


  Endpoint.post('/repositories/:repo_id/resources/:id/unpublish')
  .description("Unpublish a resource and all its sub-records and components")
  .params(["id", :id],
          ["repo_id", :repo_id])
  .permissions([:update_resource_record])
  .no_data(true)
  .returns([200, :updated],
           [400, :error]) \
  do
    resource = Resource.get_or_die(params[:id])
    resource.unpublish!

    updated_response(resource)
  end


  Endpoint.get('/repositories/:repo_id/resources/:id/models_in_graph')
    .description("Get a list of record types in the graph of a resource")
    .params(["id", :id],
            ["repo_id", :repo_id])
    .permissions([:view_repository])
    .returns([200, "OK"]) \
  do
    resource = Resource.get_or_die(params[:id])

    graph = resource.object_graph

    record_types = graph.models.map {|m| m.my_jsonmodel(true) }.compact.map {|j| j.record_type}.reject {|t| t == 'resource' }

    json_response(record_types)
  end

  ## Trees!

  Endpoint.get('/repositories/:repo_id/resources/:id/tree/root')
    .description("Fetch tree information for the top-level resource record")
    .documentation("Includes the first set of immediate children, which are grouped into 'waypoints'. Additional API requests may be required to retrieve all children if there are too many to include in the first response. See Returns below for details.")
    .params(["id", :id],
            ["repo_id", :repo_id],
            ["published_only", BooleanParam, "Whether to restrict to published/unsuppressed items", :default => false])
    .example("shell") do
      <<~SHELL
        curl -H "X-ArchivesSpace-Session: $SESSION" \\
          "http://localhost:8089/repositories/2/resources/1/tree/root"
      SHELL
    end
    .permissions([:view_repository])
    .returns([200, TreeDocs::ROOT_DOCS]) \
  do
    json_response(large_tree_for_resource.root)
  end

  Endpoint.get('/repositories/:repo_id/resources/:id/tree/waypoint')
    .description("Fetch the record slice for a given tree waypoint")
    .params(["id", :id],
            ["repo_id", :repo_id],
            ["offset", Integer, "The page of records to return"],
            ["parent_node", String, "The URI of the parent of this waypoint (none for the root record)", :optional => true],
            ["published_only", BooleanParam, "Whether to restrict to published/unsuppressed items", :default => false])
    .example("shell") do
      <<~SHELL
        curl -H "X-ArchivesSpace-Session: $SESSION" \\
          "http://localhost:8089/repositories/2/resources/1/tree/waypoint?offset=0&parent_node=/repositories/2/archival_objects/1"
      SHELL
    end
    .permissions([:view_repository])
    .returns([200, TreeDocs::WAYPOINT_DOCS]) \
  do
    offset = params[:offset]

    parent_id = if params[:parent_node]
                  JSONModel.parse_reference(params[:parent_node]).fetch(:id)
                else
                  # top-level record
                  nil
                end

    json_response(large_tree_for_resource.waypoint(parent_id, offset))
  end

  Endpoint.get('/repositories/:repo_id/resources/:id/tree/node')
    .description("Fetch tree information for an Archival Object record within a tree")
    .params(["id", :id],
            ["repo_id", :repo_id],
            ["node_uri", String, "The URI of the Archival Object record of interest"],
            ["published_only", BooleanParam, "Whether to restrict to published/unsuppressed items", :default => false])
    .example("shell") do
      <<~SHELL
        curl -H "X-ArchivesSpace-Session: $SESSION" \\
          "http://localhost:8089/repositories/2/resources/1/tree/node?node_uri=/repositories/2/archival_objects/1"
      SHELL
    end
    .permissions([:view_repository])
    .returns([200, TreeDocs::NODE_DOCS]) \
  do
    ao_id = JSONModel.parse_reference(params[:node_uri]).fetch(:id)

    json_response(large_tree_for_resource.node(ArchivalObject.get_or_die(ao_id)))
  end

  Endpoint.get('/repositories/:repo_id/resources/:id/tree/node_from_root')
    .description("Fetch tree paths from the root record to Archival Objects")
    .params(["id", :id],
            ["repo_id", :repo_id],
            ["node_ids", [Integer], "The IDs of the Archival Object records of interest"],
            ["published_only", BooleanParam, "Whether to restrict to published/unsuppressed items", :default => false])
    .example("shell") do
      <<~SHELL
        curl -H "X-ArchivesSpace-Session: $SESSION" \\
          "http://localhost:8089/repositories/2/resources/1/tree/node_from_root?node_ids[]=1"
      SHELL
    end
    .permissions([:view_repository])
    .returns([200, TreeDocs::NODE_FROM_ROOT_DOCS]) \
  do
    json_response(large_tree_for_resource.node_from_root(params[:node_ids], params[:repo_id]))
  end

  private

  def large_tree_for_resource(largetree_opts = {})
    resource = Resource.get_or_die(params[:id])

    large_tree = LargeTree.new(resource, {:published_only => params[:published_only]}.merge(largetree_opts))
    large_tree.add_decorator(LargeTreeResource.new)

    large_tree
  end

end
