# frozen_string_literal: true

module Api
  module V1
    ##
    # ImportController is responsible for importing game data (e.g. deck data)
    # and creating a new Party along with associated records (job, characters, weapons, summons, etc.).
    #
    # The controller expects a JSON payload whose top-level key is "import". If not wrapped,
    # the controller will wrap the raw data automatically.
    #
    # @example Valid payload structure
    #   {
    #     "import": {
    #       "deck": { "name": "My Party", ... },
    #       "pc": { "job": { "master": { "name": "Warrior" } }, ... }
    #     }
    #   }
    class ImportController < Api::V1::ApiController
      ELEMENT_MAPPING = {
        0 => nil,
        1 => 4,
        2 => 2,
        3 => 3,
        4 => 1,
        5 => 6,
        6 => 5
      }.freeze

      # GBF series_id to CharacterSeries slug mapping
      GBF_SERIES_TO_SLUG = {
        1 => 'summer',
        2 => 'yukata',
        3 => 'valentine',
        4 => 'halloween',
        5 => 'holiday',
        6 => 'zodiac',
        7 => 'grand',
        8 => 'fantasy',
        9 => 'collab',
        10 => 'eternal',
        11 => 'evoker',
        12 => 'saint',
        13 => 'formal'
      }.freeze

      # GBF series_id to WeaponSeries slug mapping
      GBF_WEAPON_SERIES_TO_SLUG = {
        1 => 'seraphic',
        2 => 'grand',
        3 => 'dark-opus',
        4 => 'revenant',
        5 => 'primal',
        6 => 'beast',
        7 => 'regalia',
        8 => 'omega',
        9 => 'olden-primal',
        10 => 'hollowsky',
        11 => 'xeno',
        12 => 'rose',
        13 => 'ultima',
        14 => 'bahamut',
        15 => 'epic',
        16 => 'cosmos',
        17 => 'superlative',
        18 => 'vintage',
        19 => 'class-champion',
        20 => 'replica',
        21 => 'relic',
        22 => 'rusted',
        23 => 'sephira',
        24 => 'vyrmament',
        25 => 'upgrader',
        26 => 'astral',
        27 => 'draconic',
        28 => 'eternal-splendor',
        29 => 'ancestral',
        30 => 'new-world-foundation',
        31 => 'ennead',
        32 => 'militis',
        33 => 'malice',
        34 => 'menace',
        35 => 'illustrious',
        36 => 'proven',
        37 => 'revans',
        38 => 'world',
        39 => 'exo',
        40 => 'draconic-providence',
        41 => 'celestial',
        42 => 'omega-rebirth',
        43 => 'collab',
        44 => 'destroyer'
      }.freeze

      # GBF series_id to SummonSeries slug mapping
      GBF_SUMMON_SERIES_TO_SLUG = {
        1 => 'providence',
        2 => 'genesis',
        3 => 'magna',
        4 => 'optimus',
        5 => 'demi-optimus',
        6 => 'archangel',
        7 => 'arcarum',
        8 => 'epic',
        9 => 'carbuncle',
        10 => 'dynamis',
        12 => 'cryptid',
        13 => 'six-dragons',
        14 => 'summer',
        15 => 'yukata',
        16 => 'holiday',
        17 => 'collab',
        18 => 'bellum',
        19 => 'crest',
        20 => 'robur'
      }.freeze

      before_action :ensure_admin_role, only: %i[weapons summons characters]

      ##
      # Processes an import request.
      #
      # It reads and parses the raw JSON, wraps the data under the "import" key if necessary,
      # transforms the deck data using BaseDeckTransformer, validates that the transformed data
      # contains required fields, and then creates a new Party record (and its associated objects)
      # inside a transaction.
      #
      # @return [void] Renders JSON response with a party shortcode or an error message.
      def create
        Rails.logger.info '[IMPORT] Checking input...'

        body = parse_request_body
        return unless body

        raw_params = body['import']
        unless raw_params.is_a?(Hash)
          Rails.logger.error "[IMPORT] 'import' key is missing or not a hash."
          return render json: { error: 'Invalid JSON data' }, status: :unprocessable_content
        end

        unless raw_params['deck'].is_a?(Hash) &&
               raw_params['deck'].key?('pc') &&
               raw_params['deck'].key?('npc')
          Rails.logger.error '[IMPORT] Deck data incomplete or missing.'
          return render json: { error: 'Invalid deck data' }, status: :unprocessable_content
        end

        Rails.logger.info '[IMPORT] Starting import...'

        return if performed? # Rendered an error response already

        party = Party.create(user: current_user)
        deck_data = raw_params['import']
        process_data(party, deck_data)

        render json: { shortcode: party.shortcode }, status: :created
      rescue StandardError => e
        render json: { error: e.message }, status: :unprocessable_content
      end

      def weapons
        Rails.logger.info '[IMPORT] Checking weapon gamedata input...'

        body = parse_request_body
        return unless body

        weapon = Weapon.find_by(granblue_id: body['id'])
        unless weapon
          Rails.logger.error "[IMPORT] Weapon not found with ID: #{body['id']}"
          return render json: { error: 'Weapon not found' }, status: :not_found
        end

        lang = params[:lang]
        unless %w[en jp].include?(lang)
          Rails.logger.error "[IMPORT] Invalid language: #{lang}"
          return render json: { error: 'Invalid language' }, status: :unprocessable_content
        end

        begin
          weapon.update!(
            "game_raw_#{lang}" => body.to_json
          )

          # Parse series_id and assign WeaponSeries
          series_id = body['series_id'] || body.dig('master', 'series_id')
          if series_id
            slug = GBF_WEAPON_SERIES_TO_SLUG[series_id.to_i]
            if slug
              series_record = WeaponSeries.find_by(slug: slug)
              if series_record && weapon.weapon_series != series_record
                weapon.update!(weapon_series: series_record)
                Rails.logger.info "[IMPORT] Set series '#{slug}' for weapon #{weapon.granblue_id}"
              end
            end
          end

          render json: { message: 'Weapon gamedata updated successfully' }, status: :ok
        rescue StandardError => e
          Rails.logger.error "[IMPORT] Failed to update weapon gamedata: #{e.message}"
          render json: { error: e.message }, status: :unprocessable_content
        end
      end

      def summons
        Rails.logger.info '[IMPORT] Checking summon gamedata input...'

        body = parse_request_body
        return unless body

        summon = Summon.find_by(granblue_id: body['id'])
        unless summon
          Rails.logger.error "[IMPORT] Summon not found with ID: #{body['id']}"
          return render json: { error: 'Summon not found' }, status: :not_found
        end

        lang = params[:lang]
        unless %w[en jp].include?(lang)
          Rails.logger.error "[IMPORT] Invalid language: #{lang}"
          return render json: { error: 'Invalid language' }, status: :unprocessable_content
        end

        begin
          summon.update!(
            "game_raw_#{lang}" => body.to_json
          )

          # Parse series_id and assign SummonSeries
          series_id = body['series_id'] || body.dig('master', 'series_id')
          if series_id
            slug = GBF_SUMMON_SERIES_TO_SLUG[series_id.to_i]
            if slug
              series_record = SummonSeries.find_by(slug: slug)
              if series_record && summon.summon_series != series_record
                summon.update!(summon_series: series_record)
                Rails.logger.info "[IMPORT] Set series '#{slug}' for summon #{summon.granblue_id}"
              end
            end
          end

          render json: { message: 'Summon gamedata updated successfully' }, status: :ok
        rescue StandardError => e
          Rails.logger.error "[IMPORT] Failed to update summon gamedata: #{e.message}"
          render json: { error: e.message }, status: :unprocessable_content
        end
      end

      ##
      # Updates character gamedata from JSON blob.
      #
      # @return [void] Renders JSON response with success or error message.
      def characters
        Rails.logger.info '[IMPORT] Checking character gamedata input...'

        body = parse_request_body
        return unless body

        character = Character.find_by(granblue_id: body['id'])
        unless character
          Rails.logger.error "[IMPORT] Character not found with ID: #{body['id']}"
          return render json: { error: 'Character not found' }, status: :not_found
        end

        lang = params[:lang]
        unless %w[en jp].include?(lang)
          Rails.logger.error "[IMPORT] Invalid language: #{lang}"
          return render json: { error: 'Invalid language' }, status: :unprocessable_content
        end

        begin
          character.update!(
            "game_raw_#{lang}" => body.to_json
          )

          # Parse series_id and create CharacterSeriesMembership
          series_id = body['series_id'] || body.dig('master', 'series_id')
          if series_id
            slug = GBF_SERIES_TO_SLUG[series_id.to_i]
            if slug
              series_record = CharacterSeries.find_by(slug: slug)
              if series_record && !character.character_series_records.include?(series_record)
                character.character_series_memberships.create!(character_series: series_record)
                Rails.logger.info "[IMPORT] Added series '#{slug}' to character #{character.granblue_id}"
              end
            end
          end

          render json: { message: 'Character gamedata updated successfully' }, status: :ok
        rescue StandardError => e
          Rails.logger.error "[IMPORT] Failed to update character gamedata: #{e.message}"
          render json: { error: e.message }, status: :unprocessable_content
        end
      end

      private

      ##
      # Ensures the current user has admin role (role 9).
      # Renders an error if the user is not an admin.
      #
      # @return [void]
      def ensure_admin_role
        return if current_user&.role == 9

        Rails.logger.error "[IMPORT] Unauthorized access attempt by user #{current_user&.id}"
        render json: { error: 'Unauthorized' }, status: :unauthorized
      end

      ##
      # Reads and parses the raw JSON request body.
      #
      # @return [Hash] Parsed JSON data.
      # @raise [JSON::ParserError] If the JSON is invalid.
      def parse_request_body
        raw_body = request.raw_post
        JSON.parse(raw_body)
      rescue JSON::ParserError => e
        Rails.logger.error "[IMPORT] Invalid JSON: #{e.message}"
        render json: { error: 'Invalid JSON data' }, status: :bad_request and return
      end

      ##
      # Ensures that the provided data is wrapped under an "import" key.
      #
      # @param data [Hash] The parsed JSON data.
      # @return [Hash] Data wrapped under the "import" key.
      def wrap_import_data(data)
        data.key?('import') ? data : { 'import' => data }
      end

      ##
      # Processes the deck data using processors.
      #
      # @param party [Party] The party to insert data into
      # @param data [Hash] The wrapped data.
      # @return [Hash] The transformed deck data.
      def process_data(party, data)
        Rails.logger.info '[IMPORT] Transforming deck data'

        Processors::JobProcessor.new(party, data).process
        Processors::CharacterProcessor.new(party, data).process
        Processors::SummonProcessor.new(party, data).process
        Processors::WeaponProcessor.new(party, data).process
      end
    end
  end
end
