require_relative '../du/upload_file'
require_relative 'iap_status'
require_relative 'iap_type'
require_relative 'tunes_base'

module Spaceship
  module Tunes
    class IAPDetail < TunesBase
      # @return (Spaceship::Tunes::Application) A reference to the application
      attr_accessor :application

      # @return (Integer) the IAP id
      attr_accessor :purchase_id

      # @return (Bool) if it is a news subscription
      attr_accessor :is_news_subscription

      # @return (String) the IAP Referencename
      attr_accessor :reference_name

      # @return (String) the IAP Product-Id
      attr_accessor :product_id

      # @return (String) free trial period
      attr_accessor :subscription_free_trial

      # @return (String) subscription duration
      attr_accessor :subscription_duration

      # @return (Bool) Cleared for sale flag
      attr_accessor :cleared_for_sale

      # @return (Hash) app store promotion image (optional)
      attr_accessor :merch_screenshot

      # @return (Hash) app review screenshot (required)
      attr_accessor :review_screenshot

      # @return (String) the notes for the review team
      attr_accessor :review_notes

      # @return (Hash) subscription pricing target
      attr_accessor :subscription_price_target

      # @return (Hash) Relevant only for recurring subscriptions. Holds pricing related data, such
      # as subscription pricing, intro offers, etc.
      attr_accessor :raw_pricing_data

      attr_mapping({
        'adamId' => :purchase_id,
        'referenceName.value' => :reference_name,
        'productId.value' => :product_id,
        'isNewsSubscription' => :is_news_subscription,
        'pricingDurationType.value' => :subscription_duration,
        'freeTrialDurationType.value' => :subscription_free_trial,
        'clearedForSale.value' => :cleared_for_sale
      })

      def setup
        @raw_pricing_data = @raw_data["pricingData"]
        @raw_data.delete("pricingData")

        if @raw_pricing_data
          @raw_data.set(["pricingIntervals"], @raw_pricing_data["subscriptions"])
        end
      end

      # @return (Hash) Hash of languages
      # @example: {
      #   'de-DE': {
      #     name: "Name shown in AppStore",
      #     description: "Description of the In app Purchase"
      #
      #   }
      # }
      def versions
        parsed_versions = {}
        raw_versions = raw_data["versions"].first["details"]["value"]
        raw_versions.each do |localized_version|
          language = localized_version["value"]["localeCode"]
          parsed_versions[language.to_sym] = {
            name: localized_version["value"]["name"]["value"],
            description: localized_version["value"]["description"]["value"],
            id: localized_version["value"]["id"],
            status: localized_version["value"]["status"]
          }
        end
        return parsed_versions
      end

      # transforms user-set versions to iTC ones
      def versions=(value = {})
        if value.kind_of?(Array)
          # input that comes from iTC api
          return
        end
        new_versions = []
        value.each do |language, current_version|
          new_versions << {
            "value" =>   {
              "name" =>  { "value" => current_version[:name] },
              "description" =>  { "value" => current_version[:description] },
              "localeCode" => language.to_s,
              "id" => current_version[:id]
            }
          }
        end

        raw_data.set(["versions"], [{ reviewNotes: { value: @review_notes }, "contentHosting" => raw_data['versions'].first['contentHosting'], "details" => { "value" => new_versions }, "id" => raw_data["versions"].first["id"], "reviewScreenshot" => { "value" => review_screenshot } }])
      end

      # transforms user-set intervals to iTC ones
      def pricing_intervals=(value = [])
        raw_pricing_intervals =
          client.transform_to_raw_pricing_intervals(application.apple_id, self.purchase_id, value)
        raw_data.set(["pricingIntervals"], raw_pricing_intervals)
        @raw_pricing_data["subscriptions"] = raw_pricing_intervals if @raw_pricing_data
      end

      # @return (Array) pricing intervals
      # @example:
      #  [
      #    {
      #      country: "WW",
      #      begin_date: nil,
      #      end_date: nil,
      #      tier: 1
      #    }
      #  ]
      def pricing_intervals
        @pricing_intervals ||= (raw_data["pricingIntervals"] || @raw_pricing_data["subscriptions"] || []).map do |interval|
          {
            tier: interval["value"]["tierStem"].to_i,
            begin_date: interval["value"]["priceTierEffectiveDate"],
            end_date: interval["value"]["priceTierEndDate"],
            grandfathered: interval["value"]["grandfathered"],
            country: interval["value"]["country"]
          }
        end
      end

      # @return (String) Human Readable type of the purchase
      def type
        Tunes::IAPType.get_from_string(raw_data["addOnType"])
      end

      # @return (String) Human Readable status of the purchase
      def status
        Tunes::IAPStatus.get_from_string(raw_data["versions"].first["status"])
      end

      # @return (Hash) Hash containing existing promotional image data
      def merch_screenshot
        return nil unless raw_data && raw_data["versions"] && raw_data["versions"].first && raw_data["versions"].first["merch"] && raw_data["versions"].first["merch"]["images"].first["image"]["value"]
        raw_data["versions"].first["merch"]["images"].first["image"]["value"]
      end

      # @return (Hash) Hash containing existing review screenshot data
      def review_screenshot
        return nil unless raw_data && raw_data["versions"] && raw_data["versions"].first && raw_data["versions"].first["reviewScreenshot"] && raw_data['versions'].first["reviewScreenshot"]["value"]
        raw_data['versions'].first['reviewScreenshot']['value']
      end

      # Saves the current In-App-Purchase
      def save!
        # Transform localization versions back to original format.
        versions_array = []
        versions.each do |language, value|
          versions_array << {
                    "value" =>  {
                      "description" => { "value" => value[:description] },
                      "name" => { "value" => value[:name] },
                      "localeCode" => language.to_s,
                      "id" => value[:id]
                    }
          }
        end

        raw_data.set(["versions"], [{ reviewNotes: { value: @review_notes }, contentHosting: raw_data['versions'].first['contentHosting'], "details" => { "value" => versions_array }, id: raw_data["versions"].first["id"], reviewScreenshot: { "value" => review_screenshot } }])

        # transform pricingDetails
        raw_pricing_intervals =
          client.transform_to_raw_pricing_intervals(application.apple_id,
                                                    self.purchase_id, pricing_intervals,
                                                    subscription_price_target)
        raw_data.set(["pricingIntervals"], raw_pricing_intervals)
        @raw_pricing_data["subscriptions"] = raw_pricing_intervals if @raw_pricing_data

        if @merch_screenshot
          # Upload App Store Promotional image (Optional)
          upload_file = UploadFile.from_path(@merch_screenshot)
          merch_data = client.upload_purchase_merch_screenshot(application.apple_id, upload_file)
          raw_data["versions"][0]["merch"] = merch_data
        end

        if @review_screenshot
          # Upload Screenshot
          upload_file = UploadFile.from_path(@review_screenshot)
          screenshot_data = client.upload_purchase_review_screenshot(application.apple_id, upload_file)
          raw_data["versions"][0]["reviewScreenshot"] = screenshot_data
        end
        # Update the Purchase
        client.update_iap!(app_id: application.apple_id, purchase_id: self.purchase_id, data: raw_data)

        # Update pricing for a recurring subscription.
        if raw_data["addOnType"] == Spaceship::Tunes::IAPType::RECURRING
          client.update_recurring_iap_pricing!(app_id: application.apple_id, purchase_id: self.purchase_id,
                                               pricing_intervals: raw_data["pricingIntervals"])
        end
      end

      # Deletes In-App-Purchase
      def delete!
        client.delete_iap!(app_id: application.apple_id, purchase_id: self.purchase_id)
      end

      # Retrieves the actual prices for an iap.
      #
      # @return ([]) An empty array
      #   if the iap is not yet cleared for sale
      # @return ([Spaceship::Tunes::PricingInfo]) An array of pricing infos from the same pricing tier
      #   if the iap uses world wide pricing
      # @return ([Spaceship::Tunes::IAPSubscriptionPricingInfo]) An array of pricing infos from multple subscription pricing tiers
      #   if the iap uses territorial pricing
      def pricing_info
        return [] unless cleared_for_sale
        return world_wide_pricing_info if world_wide_pricing?
        territorial_pricing_info
      end

      private

      # Checks wheather an iap uses world wide or territorial pricing.
      #
      # @return (true, false)
      def world_wide_pricing?
        pricing_intervals.fetch(0, {})[:country] == "WW"
      end

      # Maps a single pricing interval to pricing infos.
      #
      # @return ([Spaceship::Tunes::PricingInfo]) An array of pricing infos from the same tier
      def world_wide_pricing_info
        client
          .pricing_tiers
          .find { |p| p.tier_stem == pricing_intervals.first[:tier].to_s }
          .pricing_info
      end

      # Maps pricing intervals to their respective subscription pricing infos.
      #
      # @return ([Spaceship::Tunes::IAPSubscriptionPricingInfo]) An array of subscription pricing infos
      def territorial_pricing_info
        pricing_matrix = client.subscription_pricing_tiers(application.apple_id)
        pricing_intervals.map do |interval|
          pricing_matrix
            .find { |p| p.tier_stem == interval[:tier].to_s }
            .pricing_info
            .find { |i| i.country_code == interval[:country] }
        end
      end
    end
  end
end
