class Api::ReleasesController < Api::ApiController
  include ProjectContext
  include CreateReleasableJobs

  before_action :set_project, only: [:index]
  before_action :set_release, only: [:show, :update]
  before_action :set_template_and_project, only: [:create]

  def index
    render jsonapi: releases, class: { "#{model_name.camelize}": index_serializable }
  end

  def show
    handle_response(@release)
  end

  def create
    params_without_photo = release_create_params.except(:photos, :person_photo)
    release = filtered_releases.new(params_without_photo)
    release.project_id = @project.id
    release.contract_template_id = @contract_template.id
    handle_attachments(release, release_create_params[:photos])
    release.save!(context: :native)
    log_create_analytics
    after_create(release)
    handle_response(release, :created)
  end

  def update
    if model_name == "acquired_media_release"
      authorize @release, :update_file_infos?

      @release.attributes = release_create_params
      @release.save!
    else
      authorize @release, :update_photos?

      handle_attachments(@release, release_create_params[:photos])
      @release.save!
    end
    
    handle_response(@release)
  end

  def handle_response(release, status = :ok)
    if model_name == "acquired_media_release"
      mapping = {
        "#{model_name.camelize}": SerializableAcquiredMediaRelease,
        FileInfo: SerializableFileInfo
      }

      render jsonapi: release,
           status: status,
           class: mapping,
           include: [:file_infos]
    else
      mapping = {
        "#{model_name.camelize}": show_serializable,
        "ActiveStorage::Attachment".to_sym => ActiveStorage::SerializableAttachment,
      }

      render jsonapi: release,
           status: status,
           class: mapping,
           include: [:photos, :guardian_photos]
    end
  end

  def model_name
    raise "Please specify model name underscored and lowercase e.g. appearance_release"
  end

  def model_constant
    model_name.camelize.constantize
  end

  def attributes_for_index
    raise "Please specify attributes for index"
  end

  def index_serializable
    name = model_name
    attributes_to_send = attributes_for_index
    Class.new(JSONAPI::Serializable::Resource) do
      type name

      attributes_to_send.each do |attr|
        attribute attr.to_sym
      end
    end
  end

  def show_serializable
    name = model_name
    constant = model_constant
    Class.new(JSONAPI::Serializable::Resource) do
      type name

      constant.new.attributes.keys.each do |attr|
        attribute attr.to_sym
      end

      if ["appearance_release", "talent_release"].include?(name)
        has_many :guardian_photos do
          data do
            [@object.guardian_photo.try(:attachment)].compact
          end
          meta do
            { count: @object.try(:guardian_photo).try(:attached?) ? 1 : 0  }
          end
        end
      end

      unless name == "acquired_media_release"
        has_many :photos do
          if name == "appearance_release"
            data do
              [@object.photo.attachment]
            end
          end
          meta do
            { count: @object.photos.size }
          end
        end
      end
    end
  end

  def create_deserializable
    constant = model_constant
    Class.new(JSONAPI::Deserializable::Resource) do
      constant.new.attributes.keys.except(:created_at, :updated_at, :id, :user_id).each do |attr|
        attribute attr.to_sym
      end
    end
  end

  def handle_attachments(release, params_photos)
    photos = params_photos || []
    photos.each do |photo|
      photo[:io] = StringIO.new(Base64.decode64(photo[:io]))
      release.photos.attach(io: photo[:io], filename: photo[:filename])
    end
  end

  private

  def releases
    date = Date.parse(params.fetch(:updated_since, Date.new.to_s))
    table = model_constant.table_name
    policy_scope(model_constant.where(["#{table}.project_id = ? and #{table}.updated_at >= ?", @project.id, date]))
  end

  def set_release
    @release = model_constant.find(params[:id])
  end

  def set_template_and_project
    @contract_template = ContractTemplate.find(params[:contract_template_id])
    @project = @contract_template.project
  end

  def filtered_releases
    if @project.present?
      policy_scope(@project.public_send(model_name.pluralize))
    else
      releases
    end
  end

  def release_create_params
    parameters = params.require(model_name).permit!
    keys = model_constant.new.attributes.keys + [:guardian_photo, :person_photo, :photos, :signature, :signature_base64, :file_infos_attributes]
    parameters[:signature_base64] = parameters[:signature]
    parameters = parameters.slice(*keys).except(:created_at, :updated_at, :id, :user_id, :signature)
    parameters
  end

  def log_create_analytics
    TrackAnalyticsJob.perform_later(Current.user, Current.account, :track_create_native_release, release_type: model_constant.to_s, account: @account, user_agent: request.user_agent, user_ip: request.remote_ip, application: :ios)
  end
end
