# frozen_string_literal: true

class IndexaCapitalItemsController < ApplicationController
  ALLOWED_ACCOUNTABLE_TYPES = %w[Depository CreditCard Investment Loan OtherAsset OtherLiability Crypto Property Vehicle].freeze

  before_action :set_indexa_capital_item, only: [ :show, :edit, :update, :destroy, :sync, :setup_accounts, :complete_account_setup ]
  before_action :require_admin!, only: [ :new, :create, :preload_accounts, :select_accounts, :link_accounts, :select_existing_account, :link_existing_account, :edit, :update, :destroy, :sync, :setup_accounts, :complete_account_setup ]

  def index
    @indexa_capital_items = Current.family.indexa_capital_items.ordered
  end

  def show
  end

  def new
    @indexa_capital_item = Current.family.indexa_capital_items.build
  end

  def edit
  end

  def create
    @indexa_capital_item = Current.family.indexa_capital_items.build(indexa_capital_item_params)
    @indexa_capital_item.name ||= "IndexaCapital Connection"

    if @indexa_capital_item.save
      if turbo_frame_request?
        flash.now[:notice] = t(".success", default: "Successfully configured IndexaCapital.")
        @indexa_capital_items = Current.family.indexa_capital_items.ordered
        render turbo_stream: [
          turbo_stream.replace(
            "indexa_capital-providers-panel",
            partial: "settings/providers/indexa_capital_panel",
            locals: { indexa_capital_items: @indexa_capital_items }
          ),
          *flash_notification_stream_items
        ]
      else
        redirect_to settings_providers_path, notice: t(".success"), status: :see_other
      end
    else
      @error_message = @indexa_capital_item.errors.full_messages.join(", ")

      if turbo_frame_request?
        render turbo_stream: turbo_stream.replace(
          "indexa_capital-providers-panel",
          partial: "settings/providers/indexa_capital_panel",
          locals: { error_message: @error_message }
        ), status: :unprocessable_entity
      else
        redirect_to settings_providers_path, alert: @error_message, status: :unprocessable_entity
      end
    end
  end

  def update
    if @indexa_capital_item.update(indexa_capital_item_params)
      if turbo_frame_request?
        flash.now[:notice] = t(".success", default: "Successfully updated IndexaCapital configuration.")
        @indexa_capital_items = Current.family.indexa_capital_items.ordered
        render turbo_stream: [
          turbo_stream.replace(
            "indexa_capital-providers-panel",
            partial: "settings/providers/indexa_capital_panel",
            locals: { indexa_capital_items: @indexa_capital_items }
          ),
          *flash_notification_stream_items
        ]
      else
        redirect_to settings_providers_path, notice: t(".success"), status: :see_other
      end
    else
      @error_message = @indexa_capital_item.errors.full_messages.join(", ")

      if turbo_frame_request?
        render turbo_stream: turbo_stream.replace(
          "indexa_capital-providers-panel",
          partial: "settings/providers/indexa_capital_panel",
          locals: { error_message: @error_message }
        ), status: :unprocessable_entity
      else
        redirect_to settings_providers_path, alert: @error_message, status: :unprocessable_entity
      end
    end
  end

  def destroy
    @indexa_capital_item.destroy_later
    redirect_to settings_providers_path, notice: t(".success", default: "Scheduled IndexaCapital connection for deletion.")
  end

  def sync
    unless @indexa_capital_item.syncing?
      @indexa_capital_item.sync_later
    end

    respond_to do |format|
      format.html { redirect_back_or_to accounts_path }
      format.json { head :ok }
    end
  end

  # Collection actions for account linking flow

  def preload_accounts
    # Trigger a sync to fetch accounts from the provider
    indexa_capital_item = Current.family.indexa_capital_items.first
    unless indexa_capital_item&.credentials_configured?
      redirect_to settings_providers_path, alert: t(".no_credentials_configured")
      return
    end

    indexa_capital_item.sync_later unless indexa_capital_item.syncing?
    redirect_to select_accounts_indexa_capital_items_path(accountable_type: params[:accountable_type], return_to: params[:return_to])
  end

  def select_accounts
    @accountable_type = params[:accountable_type]
    @return_to = params[:return_to]

    indexa_capital_item = Current.family.indexa_capital_items.first
    unless indexa_capital_item&.credentials_configured?
      redirect_to settings_providers_path, alert: t(".no_credentials_configured")
      return
    end

    # Always fetch fresh data (accounts + balances) when user visits this page
    fetch_accounts_synchronously(indexa_capital_item)

    @indexa_capital_accounts = indexa_capital_item.indexa_capital_accounts
                                                .left_joins(:account_provider)
                                                .where(account_providers: { id: nil })
                                                .order(:name)
  end

  def link_accounts
    indexa_capital_item = Current.family.indexa_capital_items.first
    unless indexa_capital_item&.credentials_configured?
      redirect_to settings_providers_path, alert: t(".no_api_key")
      return
    end

    selected_ids = params[:selected_account_ids] || []
    if selected_ids.empty?
      redirect_to select_accounts_indexa_capital_items_path, alert: t(".no_accounts_selected")
      return
    end

    accountable_type = params[:accountable_type] || "Depository"
    created_count = 0
    already_linked_count = 0
    invalid_count = 0

    indexa_capital_item.indexa_capital_accounts.where(id: selected_ids).find_each do |indexa_capital_account|
      # Skip if already linked
      if indexa_capital_account.account_provider.present?
        already_linked_count += 1
        next
      end

      # Skip if invalid name
      if indexa_capital_account.name.blank?
        invalid_count += 1
        next
      end

      # Create Sure account and link
      link_indexa_capital_account(indexa_capital_account, accountable_type)
      created_count += 1
    rescue => e
      Rails.logger.error "IndexaCapitalItemsController#link_accounts - Failed to link account: #{e.message}"
    end

    if created_count > 0
      indexa_capital_item.sync_later unless indexa_capital_item.syncing?
      redirect_to accounts_path, notice: t(".success", count: created_count)
    else
      redirect_to select_accounts_indexa_capital_items_path, alert: t(".link_failed")
    end
  end

  def select_existing_account
    @account = Current.family.accounts.find(params[:account_id])
    @indexa_capital_item = Current.family.indexa_capital_items.first

    unless @indexa_capital_item&.credentials_configured?
      redirect_to settings_providers_path, alert: t(".no_credentials_configured")
      return
    end

    @indexa_capital_accounts = @indexa_capital_item.indexa_capital_accounts
                                                      .left_joins(:account_provider)
                                                      .where(account_providers: { id: nil })
                                                      .order(:name)
  end

  def link_existing_account
    account = Current.family.accounts.find(params[:account_id])
    indexa_capital_item = Current.family.indexa_capital_items.first

    unless indexa_capital_item&.credentials_configured?
      redirect_to settings_providers_path, alert: t(".no_api_key")
      return
    end

    indexa_capital_account = indexa_capital_item.indexa_capital_accounts.find(params[:indexa_capital_account_id])

    if indexa_capital_account.account_provider.present?
      redirect_to account_path(account), alert: t(".provider_account_already_linked")
      return
    end

    indexa_capital_account.ensure_account_provider!(account)
    indexa_capital_item.sync_later unless indexa_capital_item.syncing?

    redirect_to account_path(account), notice: t(".success", account_name: account.name)
  end

  def setup_accounts
    @unlinked_accounts = @indexa_capital_item.unlinked_indexa_capital_accounts.order(:name)
  end

  def complete_account_setup
    account_configs = params[:accounts] || {}

    if account_configs.empty?
      redirect_to setup_accounts_indexa_capital_item_path(@indexa_capital_item), alert: t(".no_accounts")
      return
    end

    created_count = 0
    skipped_count = 0

    account_configs.each do |indexa_capital_account_id, config|
      next if config[:account_type] == "skip"

      indexa_capital_account = @indexa_capital_item.indexa_capital_accounts.find_by(id: indexa_capital_account_id)
      next unless indexa_capital_account
      next if indexa_capital_account.account_provider.present?

      accountable_type = infer_accountable_type(config[:account_type], config[:subtype])
      account = create_account_from_indexa_capital(indexa_capital_account, accountable_type, config)

      if account&.persisted?
        indexa_capital_account.ensure_account_provider!(account)
        indexa_capital_account.update!(sync_start_date: config[:sync_start_date]) if config[:sync_start_date].present?
        created_count += 1
      else
        skipped_count += 1
      end
    rescue => e
      Rails.logger.error "IndexaCapitalItemsController#complete_account_setup - Error: #{e.message}"
      skipped_count += 1
    end

    if created_count > 0
      @indexa_capital_item.sync_later unless @indexa_capital_item.syncing?
      redirect_to accounts_path, notice: t(".success", count: created_count)
    elsif skipped_count > 0 && created_count == 0
      redirect_to accounts_path, notice: t(".all_skipped")
    else
      redirect_to setup_accounts_indexa_capital_item_path(@indexa_capital_item), alert: t(".creation_failed", error: "Unknown error")
    end
  end

  private

    def set_indexa_capital_item
      @indexa_capital_item = Current.family.indexa_capital_items.find(params[:id])
    end

    def indexa_capital_item_params
      params.require(:indexa_capital_item).permit(
        :name,
        :sync_start_date,
        :api_token,
        :username,
        :document,
        :password
      )
    end

    def link_indexa_capital_account(indexa_capital_account, accountable_type)
      accountable_class = validated_accountable_class(accountable_type)

      account = Current.family.accounts.create!(
        name: indexa_capital_account.name,
        balance: indexa_capital_account.current_balance || 0,
        currency: indexa_capital_account.currency || "EUR",
        accountable: accountable_class.new
      )

      account.auto_share_with_family! if Current.family.share_all_by_default?
      indexa_capital_account.ensure_account_provider!(account)
      account
    end

    def create_account_from_indexa_capital(indexa_capital_account, accountable_type, config)
      accountable_class = validated_accountable_class(accountable_type)
      accountable_attrs = {}

      # Set subtype if the accountable supports it
      if config[:subtype].present? && accountable_class.respond_to?(:subtypes)
        accountable_attrs[:subtype] = config[:subtype]
      end

      account = Current.family.accounts.create!(
        name: indexa_capital_account.name,
        balance: config[:balance].present? ? config[:balance].to_d : (indexa_capital_account.current_balance || 0),
        currency: indexa_capital_account.currency || "EUR",
        accountable: accountable_class.new(accountable_attrs)
      )

      account.auto_share_with_family! if Current.family.share_all_by_default?
      account
    end

    def infer_accountable_type(account_type, subtype = nil)
      case account_type&.downcase
      when "depository"
        "Depository"
      when "credit_card"
        "CreditCard"
      when "investment"
        "Investment"
      when "loan"
        "Loan"
      when "other_asset"
        "OtherAsset"
      when "other_liability"
        "OtherLiability"
      when "crypto"
        "Crypto"
      when "property"
        "Property"
      when "vehicle"
        "Vehicle"
      else
        "Depository"
      end
    end

    def validated_accountable_class(accountable_type)
      unless ALLOWED_ACCOUNTABLE_TYPES.include?(accountable_type)
        raise ArgumentError, "Invalid accountable type: #{accountable_type}"
      end

      accountable_type.constantize
    end

    def fetch_accounts_synchronously(indexa_capital_item)
      provider = indexa_capital_item.indexa_capital_provider
      return unless provider

      accounts_data = provider.list_accounts

      accounts_data.each do |account_data|
        account_number = account_data[:account_number].to_s
        next if account_number.blank?

        # Fetch current balance from performance endpoint
        balance = provider.get_account_balance(account_number: account_number)
        account_data[:current_balance] = balance
      rescue => e
        Rails.logger.warn "IndexaCapitalItemsController - Failed to fetch balance for #{account_number}: #{e.message}"
      end

      accounts_data.each do |account_data|
        account_number = account_data[:account_number].to_s
        next if account_number.blank?

        indexa_capital_account = indexa_capital_item.indexa_capital_accounts.find_or_initialize_by(
          indexa_capital_account_id: account_number
        )
        indexa_capital_account.upsert_from_indexa_capital!(account_data)
      end
    rescue Provider::IndexaCapital::AuthenticationError => e
      Rails.logger.error "IndexaCapitalItemsController - Auth failed during sync: #{e.message}"
      flash.now[:alert] = t("indexa_capital_items.select_accounts.api_error", message: e.message)
    rescue Provider::IndexaCapital::Error => e
      Rails.logger.error "IndexaCapitalItemsController - API error during sync: #{e.message}"
      flash.now[:alert] = t("indexa_capital_items.select_accounts.api_error", message: e.message)
    end
end
