module CamaleonCms
  class PostType < CamaleonCms::TermTaxonomy
    alias_attribute :site_id, :parent_id
    default_scope { where(taxonomy: :post_type) }

    has_many :categories, foreign_key: :parent_id, dependent: :destroy, inverse_of: :post_type_parent
    has_many :post_tags, foreign_key: :parent_id, dependent: :destroy, inverse_of: :post_type
    has_many :posts, foreign_key: :taxonomy_id, dependent: :destroy, inverse_of: :post_type
    has_many :comments, through: :posts
    has_many :posts_through_categories, foreign_key: :objectid, through: :term_relationships, source: :object
    has_many :posts_draft, class_name: 'CamaleonCms::Post', foreign_key: :taxonomy_id, dependent: :destroy,
                           inverse_of: :post_type
    has_many :field_group_taxonomy, lambda {
                                      where('object_class LIKE ?', 'PostType_%')
                                    }, class_name: 'CamaleonCms::CustomField', foreign_key: :objectid, dependent: :destroy

    belongs_to :owner, class_name: CamaManager.get_user_class_name, foreign_key: :user_id, required: false
    belongs_to :site, foreign_key: :parent_id, required: false

    scope :visible_menu, -> { where(term_group: nil) }
    scope :hidden_menu, -> { where(term_group: -1) }

    before_destroy :destroy_field_groups
    after_create :set_default_site_user_roles
    after_create :refresh_routes
    after_destroy :refresh_routes
    after_update :refresh_routes,
                 if: proc { |obj| obj.destroyed_by_association.blank? && obj.saved_change_to_attribute?(:slug) }
    before_update :default_category

    # check if current post type manage categories
    def manage_categories?
      options[:has_category] || options[:has_single_category]
    end

    # hide or show this post type on admin -> contents -> menu
    # true => enable, false => disable
    def toggle_show_for_admin_menu(flag)
      update(term_group: flag == true ? nil : -1)
    end

    # check if this post type is shown on admin -> contents -> menu
    def show_for_admin_menu?
      term_group.nil?
    end

    # check if this post type manage post tags
    def manage_tags?
      options[:has_tags]
    end

    # check if this post type permit to manage seo attrs in posts
    def manage_seo?
      get_option('has_seo', get_option('has_keywords', true))
    end

    # assign settings for this post type
    # default values: {
    #   has_category: false,
    #   has_tags: false,
    #   has_summary: true,
    #   has_content: true,
    #   has_comments: false,
    #   has_picture: true,
    #   has_template: true,
    #   has_seo: true,
    #   not_deleted: false,
    #   has_layout: false,
    #   default_layout: '',
    #   contents_route_format: 'post'
    # }
    def set_settings(settings = {})
      settings.each do |key, val|
        set_option(key, val)
      end
    end

    # set or update a setting for this post type
    def set_setting(key, value)
      set_option(key, value)
    end

    # select full_categories for the post type, include all children categories
    def full_categories
      CamaleonCms::Category.where(site_id: site_id, post_type_id: id)
    end

    # return default category for this post type
    # only return a category for post types that manage categories
    def default_category
      return unless manage_categories?

      cat = categories.find_by_slug('uncategorized')
      unless cat
        cat = categories.create({ name: 'Uncategorized', slug: 'uncategorized', parent_id: id })
        cat.set_option('not_deleted', true)
      end
      cat
    end

    # add a post for current model
    #   title: title for post,    => required
    #   content: html text content, => required
    #   thumb: image url, => default (empty). check https://camaleon.website/api-methods.html#section_fileuploads
    #   categories: [1,3,4,5],    => default (empty)
    #   tags: String comma separated, => default (empty)
    #   slug: string key for post,    => default (empty)
    #   summary: String resume (optional)  => default (empty)
    #   post_order: Integer to define the order position in the list (optional)
    #   fields: Hash of values for custom fields, sample => fields: {subtitle: 'abc', icon: 'test' } (optional)
    #   settings: Hash of post settings, sample => settings:
    #     {has_content: false, has_summary: true, default_layout: 'my_layout', default_template: 'my_template' } (optional, see more in post.set_setting(...))
    #   data_metas: {template: "", layout: ""}
    # sample: my_posttype.add_post(title: "My Title", post_order: 5, content: 'lorem_ipsum', settings: {default_template: "home/counters", has_content: false, has_seo: false, skip_fields: ["sub_tite", 'banner']}, fields: {pattern: true, bg: 'https://www.reallusion.com/de/images/3dx5/whatsnew/3dx5_features_banner_bg_02.jpg'})
    #   More samples here: https://gist.github.com/owen2345/eba9691585ed78ad6f7b52e9591357bf
    # return created post if it was created, else return errors
    def add_post(args)
      _fields = args.delete(:fields)
      _settings = args.delete(:settings)
      _summary = args.delete(:summary)
      _order_position = args.delete(:order_position)
      args[:data_categories] = _categories = args.delete(:categories)
      args[:data_tags] = args.delete(:tags)
      _thumb = args.delete(:thumb)
      p = posts.new(args)
      p.slug = site.get_valid_post_slug(p.title.parameterize) unless p.slug.present?
      if p.save!
        _settings.each { |k, v| p.set_setting(k, v) } if _settings.present?
        p.set_position(_order_position) if _order_position.present?
        p.set_summary(_summary) if _summary.present?
        p.set_thumb(_thumb) if _thumb.present?
        _fields.each { |k, v| p.save_field_value(k, v) } if _fields.present?
        p.decorate
      else
        p.errors
      end
    end

    # return all available route formats of this post type for content posts
    def contents_route_formats
      {
        'post_of_post_type' => '<code>/group/:post_type_id-:title/:slug</code><br>  (Sample: https://localhost.com/group/17-services/myservice.html)',
        'post_of_category' => '<code>/category/:category_id-:title/:slug</code><br>  (Sample: https://localhost.com/category/17-services/myservice.html)',
        'post_of_category_post_type' => '<code>/:post_type_title/category/:category_id-:title/:slug</code><br>  (Sample: https://localhost.com/services/category/17-services/myservice.html)',
        'post_of_posttype' => '<code>/:post_type_title/:slug</code><br>  (Sample: https://localhost.com/services/myservice.html)',
        'post' => '<code>/:slug</code><br>  (Sample: https://localhost.com/myservice.html)',
        'hierarchy_post' => '<code>/:parent1_slug/:parent2_slug/.../:slug</code><br>  (Sample: https://localhost.com/item-1/item-1-1/item-111.html)'
      }
    end

    # return the configuration of routes for post contents
    def contents_route_format
      get_option('contents_route_format', 'post')
    end

    # verify if this post_type support for page hierarchy (parents)
    def manage_hierarchy?
      get_option('has_parent_structure', false)
    end

    private

    # skip save_metas_options callback after save changes (inherit from taxonomy) to call from here manually
    def save_metas_options_skip
      true
    end

    # assign default roles for this post type
    # define default settings for this post type
    def set_default_site_user_roles
      set_multiple_options(
        { has_category: false, has_tags: false, has_summary: true, has_content: true, has_comments: false,
          has_picture: true, has_template: true, has_seo: true, not_deleted: false, has_layout: false,
          default_layout: '' }.merge(PluginRoutes.fixActionParameter(data_options || {}).to_sym)
      )
      site.set_default_user_roles(self)
      default_category
    end

    # destroy all custom field groups assigned to this post type
    def destroy_field_groups
      if !destroyed_by_association.present? && %w[post page].include?(slug)
        errors.add(:base, 'This post type can not be deleted.')
        return false
      end
      get_field_groups.destroy_all
    end

    # reload routes to enable this post type url, like: http://localhost/my-slug
    def refresh_routes
      PluginRoutes.reload
    end
  end
end
