class SnaptradeItemsController < ApplicationController
  before_action :set_snaptrade_item, only: [ :show, :edit, :update, :destroy, :sync, :connect, :setup_accounts, :complete_account_setup, :connections, :delete_connection, :delete_orphaned_user ]
  before_action :require_admin!, only: [ :new, :create, :preload_accounts, :select_accounts, :link_accounts, :select_existing_account, :link_existing_account, :edit, :update, :destroy, :sync, :connect, :callback, :setup_accounts, :complete_account_setup, :connections, :delete_connection, :delete_orphaned_user ]

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

  def show
  end

  def new
    @snaptrade_item = Current.family.snaptrade_items.build
  end

  def edit
  end

  def create
    @snaptrade_item = Current.family.snaptrade_items.build(snaptrade_item_params)
    @snaptrade_item.name ||= t("snaptrade_items.default_name")

    if @snaptrade_item.save
      # Register user with SnapTrade after saving credentials
      begin
        @snaptrade_item.ensure_user_registered!
      rescue => e
        Rails.logger.error "SnapTrade user registration failed: #{e.message}"
        # Don't fail the whole operation - user can retry connection later
      end

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

      if turbo_frame_request?
        render turbo_stream: turbo_stream.replace(
          "snaptrade-providers-panel",
          partial: "settings/providers/snaptrade_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 @snaptrade_item.update(snaptrade_item_params)
      if turbo_frame_request?
        flash.now[:notice] = t(".success", default: "Successfully updated SnapTrade configuration.")
        @snaptrade_items = Current.family.snaptrade_items.ordered
        render turbo_stream: [
          turbo_stream.replace(
            "snaptrade-providers-panel",
            partial: "settings/providers/snaptrade_panel",
            locals: { snaptrade_items: @snaptrade_items }
          ),
          *flash_notification_stream_items
        ]
      else
        redirect_to settings_providers_path, notice: t(".success"), status: :see_other
      end
    else
      @error_message = @snaptrade_item.errors.full_messages.join(", ")

      if turbo_frame_request?
        render turbo_stream: turbo_stream.replace(
          "snaptrade-providers-panel",
          partial: "settings/providers/snaptrade_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
    @snaptrade_item.destroy_later
    redirect_to settings_providers_path, notice: t(".success", default: "Scheduled SnapTrade connection for deletion.")
  end

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

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

  # Redirect user to SnapTrade connection portal
  def connect
    @snaptrade_item.ensure_user_registered! unless @snaptrade_item.user_registered?

    redirect_url = callback_snaptrade_items_url(item_id: @snaptrade_item.id)
    portal_url = @snaptrade_item.connection_portal_url(redirect_url: redirect_url)
    redirect_to portal_url, allow_other_host: true
  rescue ActiveRecord::Encryption::Errors::Decryption => e
    Rails.logger.error "SnapTrade decryption error for item #{@snaptrade_item.id}: #{e.class} - #{e.message}\n#{e.backtrace&.first(5)&.join("\n")}"
    redirect_to settings_providers_path, alert: t(".decryption_failed")
  rescue => e
    Rails.logger.error "SnapTrade connection error: #{e.class} - #{e.message}\n#{e.backtrace&.first(5)&.join("\n")}"
    redirect_to settings_providers_path, alert: t(".connection_failed", message: e.message)
  end

  # Handle callback from SnapTrade after user connects brokerage
  def callback
    # SnapTrade redirects back after user connects their brokerage
    # The connection is already established - we just need to sync to get the accounts
    unless params[:item_id].present?
      redirect_to settings_providers_path, alert: t(".no_item")
      return
    end

    snaptrade_item = Current.family.snaptrade_items.find_by(id: params[:item_id])

    if snaptrade_item
      # Trigger a sync to fetch the newly connected accounts
      snaptrade_item.sync_later unless snaptrade_item.syncing?
      # Redirect to accounts page - user can click "accounts need setup" badge
      # when sync completes. This avoids the auto-refresh loop issues.
      redirect_to accounts_path, notice: t(".success")
    else
      redirect_to settings_providers_path, alert: t(".no_item")
    end
  end

  # Show available accounts for linking
  def setup_accounts
    @snaptrade_accounts = @snaptrade_item.snaptrade_accounts.includes(account_provider: :account)
    @linked_accounts = @snaptrade_accounts.select { |sa| sa.current_account.present? }
    @unlinked_accounts = @snaptrade_accounts.reject { |sa| sa.current_account.present? }

    no_accounts = @unlinked_accounts.blank? && @linked_accounts.blank?

    # We trigger an initial or recovery sync if there are no accounts, we aren't currently syncing,
    # and the last attempt didn't successfully complete. (If it completed and found 0 accounts, we stop here to avoid an infinite loop.)
    latest_sync = @snaptrade_item.syncs.ordered.first
    should_sync = latest_sync.nil? || !latest_sync.completed?

    if no_accounts && !@snaptrade_item.syncing? && should_sync
      @snaptrade_item.sync_later
    end

    # Existing unlinked, visible investment/crypto accounts that could be linked instead of creating duplicates
    @linkable_accounts = Current.family.accounts
      .visible
      .where(accountable_type: %w[Investment Crypto])
      .left_joins(:account_providers)
      .where(account_providers: { id: nil })
      .order(:name)

    # Determine view state
    @syncing = @snaptrade_item.syncing?
    @waiting_for_sync = no_accounts && @syncing
    @no_accounts_found = no_accounts && !@syncing && @snaptrade_item.last_synced_at.present?
  end

  # Link selected accounts to Sure
  def complete_account_setup
    Rails.logger.info "SnapTrade complete_account_setup - params: #{params.to_unsafe_h.inspect}"
    account_ids = params[:account_ids] || []
    sync_start_dates = params[:sync_start_dates] || {}
    Rails.logger.info "SnapTrade complete_account_setup - account_ids: #{account_ids.inspect}, sync_start_dates: #{sync_start_dates.inspect}"

    linked_count = 0
    errors = []

    account_ids.each do |snaptrade_account_id|
      snaptrade_account = @snaptrade_item.snaptrade_accounts.find_by(id: snaptrade_account_id)

      unless snaptrade_account
        Rails.logger.warn "SnapTrade complete_account_setup - snaptrade_account not found for id: #{snaptrade_account_id}"
        next
      end

      if snaptrade_account.current_account.present?
        Rails.logger.info "SnapTrade complete_account_setup - snaptrade_account #{snaptrade_account_id} already linked to account #{snaptrade_account.current_account.id}"
        next
      end

      begin
        # Save sync_start_date if provided
        if sync_start_dates[snaptrade_account_id].present?
          snaptrade_account.update!(sync_start_date: sync_start_dates[snaptrade_account_id])
        end

        Rails.logger.info "SnapTrade complete_account_setup - linking snaptrade_account #{snaptrade_account_id}"
        link_snaptrade_account(snaptrade_account)
        linked_count += 1
        Rails.logger.info "SnapTrade complete_account_setup - successfully linked snaptrade_account #{snaptrade_account_id}"
      rescue => e
        Rails.logger.error "Failed to link SnapTrade account #{snaptrade_account_id}: #{e.class} - #{e.message}\n#{e.backtrace&.first(5)&.join("\n")}"
        errors << e.message
      end
    end

    Rails.logger.info "SnapTrade complete_account_setup - completed. linked_count: #{linked_count}, errors: #{errors.inspect}"

    if linked_count > 0
      # Trigger sync to process the newly linked accounts
      # Always queue the sync - if one is running, this will run after it finishes
      @snaptrade_item.sync_later

      if errors.any?
        # Partial success - some linked, some failed
        redirect_to accounts_path,
                    notice: t(".partial_success", count: linked_count, failed_count: errors.size,
                              default: "Linked #{linked_count} account(s). #{errors.size} failed to link.")
      else
        redirect_to accounts_path, notice: t(".success", count: linked_count, default: "Successfully linked #{linked_count} account(s).")
      end
    else
      if errors.any?
        # All failed
        redirect_to setup_accounts_snaptrade_item_path(@snaptrade_item),
                    alert: t(".link_failed", default: "Failed to link accounts: %{errors}", errors: errors.first)
      else
        redirect_to setup_accounts_snaptrade_item_path(@snaptrade_item),
                    alert: t(".no_accounts", default: "No accounts were selected for linking.")
      end
    end
  end

  # Fetch connections list for Turbo Frame
  def connections
    data = build_connections_list
    render partial: "snaptrade_items/connections_list", layout: false, locals: {
      connections: data[:connections],
      orphaned_users: data[:orphaned_users],
      snaptrade_item: @snaptrade_item,
      error: @error
    }
  end

  # Delete a brokerage connection
  def delete_connection
    authorization_id = params[:authorization_id]

    if authorization_id.blank?
      redirect_to settings_providers_path, alert: t(".failed", message: t(".missing_authorization_id"))
      return
    end

    # Delete all local SnaptradeAccounts for this connection (triggers cleanup job)
    accounts_deleted = @snaptrade_item.snaptrade_accounts
      .where(snaptrade_authorization_id: authorization_id)
      .destroy_all
      .size

    # If no local accounts existed (orphan), delete directly from API
    api_deletion_failed = false
    if accounts_deleted == 0
      provider = @snaptrade_item.snaptrade_provider
      creds = @snaptrade_item.snaptrade_credentials

      if provider && creds&.dig(:user_id) && creds&.dig(:user_secret)
        provider.delete_connection(
          user_id: creds[:user_id],
          user_secret: creds[:user_secret],
          authorization_id: authorization_id
        )
      else
        Rails.logger.warn "SnapTrade: Cannot delete orphaned connection #{authorization_id} - missing credentials"
        api_deletion_failed = true
      end
    end

    respond_to do |format|
      if api_deletion_failed
        format.html { redirect_to settings_providers_path, alert: t(".api_deletion_failed") }
        format.turbo_stream do
          flash.now[:alert] = t(".api_deletion_failed")
          render turbo_stream: flash_notification_stream_items
        end
      else
        format.html { redirect_to settings_providers_path, notice: t(".success") }
        format.turbo_stream { render turbo_stream: turbo_stream.remove("connection_#{authorization_id}") }
      end
    end
  rescue Provider::Snaptrade::ApiError => e
    respond_to do |format|
      format.html { redirect_to settings_providers_path, alert: t(".failed", message: e.message) }
      format.turbo_stream do
        flash.now[:alert] = t(".failed", message: e.message)
        render turbo_stream: flash_notification_stream_items
      end
    end
  end

  # Delete an orphaned SnapTrade user (and all their connections)
  def delete_orphaned_user
    user_id = params[:user_id]

    # Security: verify this is actually an orphaned user
    unless @snaptrade_item.orphaned_users.include?(user_id)
      respond_to do |format|
        format.html { redirect_to settings_providers_path, alert: t(".failed") }
        format.turbo_stream do
          flash.now[:alert] = t(".failed")
          render turbo_stream: flash_notification_stream_items
        end
      end
      return
    end

    if @snaptrade_item.delete_orphaned_user(user_id)
      respond_to do |format|
        format.html { redirect_to settings_providers_path, notice: t(".success") }
        format.turbo_stream { render turbo_stream: turbo_stream.remove("orphaned_user_#{user_id.parameterize}") }
      end
    else
      respond_to do |format|
        format.html { redirect_to settings_providers_path, alert: t(".failed") }
        format.turbo_stream do
          flash.now[:alert] = t(".failed")
          render turbo_stream: flash_notification_stream_items
        end
      end
    end
  end

  # Collection actions for account linking flow

  def preload_accounts
    snaptrade_item = Current.family.snaptrade_items.first
    if snaptrade_item
      snaptrade_item.sync_later unless snaptrade_item.syncing?
      redirect_to setup_accounts_snaptrade_item_path(snaptrade_item)
    else
      redirect_to settings_providers_path, alert: t(".not_configured", default: "SnapTrade is not configured.")
    end
  end

  def select_accounts
    @accountable_type = params[:accountable_type]
    @return_to = params[:return_to]
    snaptrade_item = Current.family.snaptrade_items.first

    if snaptrade_item
      redirect_to setup_accounts_snaptrade_item_path(snaptrade_item, accountable_type: @accountable_type, return_to: @return_to)
    else
      redirect_to settings_providers_path, alert: t(".not_configured", default: "SnapTrade is not configured.")
    end
  end

  def link_accounts
    redirect_to settings_providers_path, alert: "Use the account setup flow instead"
  end

  def select_existing_account
    @account_id = params[:account_id]
    @account = Current.family.accounts.find_by(id: @account_id)
    snaptrade_item = Current.family.snaptrade_items.first

    if snaptrade_item && @account
      @snaptrade_accounts = snaptrade_item.snaptrade_accounts
        .left_joins(:account_provider)
        .where(account_providers: { id: nil })
      render :select_existing_account
    else
      redirect_to settings_providers_path, alert: t(".not_found", default: "Account or SnapTrade configuration not found.")
    end
  end

  def link_existing_account
    account_id = params[:account_id]
    snaptrade_account_id = params[:snaptrade_account_id]
    snaptrade_item_id = params[:snaptrade_item_id]

    account = Current.family.accounts.find_by(id: account_id)
    snaptrade_item = Current.family.snaptrade_items.find_by(id: snaptrade_item_id)
    snaptrade_account = snaptrade_item&.snaptrade_accounts&.find_by(id: snaptrade_account_id)

    if account && snaptrade_account
      begin
        # Create AccountProvider linking - pass the account directly
        provider = snaptrade_account.ensure_account_provider!(account)

        unless provider
          raise "Failed to create AccountProvider link"
        end

        # Trigger sync to process the linked account
        snaptrade_item.sync_later unless snaptrade_item.syncing?

        redirect_to account_path(account), notice: t(".success", default: "Successfully linked to SnapTrade account.")
      rescue => e
        Rails.logger.error "Failed to link existing account: #{e.message}"
        redirect_to settings_providers_path, alert: t(".failed", default: "Failed to link account: #{e.message}")
      end
    else
      redirect_to settings_providers_path, alert: t(".not_found", default: "Account not found.")
    end
  end

  private

    def set_snaptrade_item
      @snaptrade_item = Current.family.snaptrade_items.find(params[:id])
    end

    def snaptrade_item_params
      params.require(:snaptrade_item).permit(
        :name,
        :sync_start_date,
        :client_id,
        :consumer_key
      )
    end

    def build_connections_list
      # Fetch connections for current user from API
      api_connections = @snaptrade_item.fetch_connections

      # Get local accounts grouped by authorization_id
      local_accounts = @snaptrade_item.snaptrade_accounts
        .includes(:account_provider)
        .group_by(&:snaptrade_authorization_id)

      # Build unified list
      result = { connections: [], orphaned_users: [] }

      # Add connections from API for current user
      api_connections.each do |api_conn|
        auth_id = api_conn.id
        local_accts = local_accounts[auth_id] || []

        result[:connections] << {
          authorization_id: auth_id,
          brokerage_name: api_conn.brokerage&.name || I18n.t("snaptrade_items.connections.unknown_brokerage"),
          brokerage_slug: api_conn.brokerage&.slug,
          accounts: local_accts.map { |acct|
            { id: acct.id, name: acct.name, linked: acct.account_provider.present? }
          },
          orphaned_connection: local_accts.empty?
        }
      end

      # Add orphaned users (users registered but not current)
      orphaned = @snaptrade_item.orphaned_users
      orphaned.each do |user_id|
        result[:orphaned_users] << {
          user_id: user_id,
          display_name: user_id.truncate(30)
        }
      end

      result
    rescue Provider::Snaptrade::ApiError => e
      @error = e.message
      { connections: [], orphaned_users: [] }
    end

    def link_snaptrade_account(snaptrade_account)
      # Determine account type based on SnapTrade account type
      accountable_type = infer_accountable_type(snaptrade_account.account_type)

      # Create the Sure account
      account = Current.family.accounts.create!(
        name: snaptrade_account.name,
        balance: snaptrade_account.current_balance || 0,
        cash_balance: snaptrade_account.cash_balance || 0,
        currency: snaptrade_account.currency || Current.family.currency,
        accountable: accountable_type.constantize.new
      )

      # Link via AccountProvider - pass the account directly
      provider = snaptrade_account.ensure_account_provider!(account)

      unless provider
        Rails.logger.error "SnapTrade: Failed to create AccountProvider for snaptrade_account #{snaptrade_account.id}"
        raise "Failed to link account"
      end

      account
    end

    def infer_accountable_type(snaptrade_type)
      # SnapTrade account types: https://docs.snaptrade.com/reference/get_accounts
      case snaptrade_type&.downcase
      when "tfsa", "rrsp", "rrif", "resp", "rdsp", "lira", "lrsp", "lif", "rlsp", "prif",
           "401k", "403b", "457b", "ira", "roth_ira", "roth_401k", "sep_ira", "simple_ira",
           "pension", "retirement", "registered"
        "Investment" # Tax-advantaged accounts
      when "margin", "cash", "non-registered", "individual", "joint"
        "Investment" # Standard brokerage accounts
      when "crypto"
        "Crypto"
      else
        "Investment" # Default to Investment for brokerage accounts
      end
    end
end
