/**
 * InputsSelectStep — lets the user select UTXOs to fund the invitation.
 *
 * On mount, queries for suitable resources via the invitation's `findSuitableResources`.
 * Auto-selects greedily, then lets the user toggle individual UTXOs.
 * Shows required, selected, and change amounts.
 */

import React, { useState, useEffect, useCallback, useRef } from 'react';
import { Box, Text } from 'ink';
import { colors, formatSatoshis } from '../../../../theme.js';
import { useSatoshisConversion } from '../../../../hooks/useSatoshisConversion.js';
import { useLayeredInput } from '../../../../hooks/useInputLayer.js';
import type { InputsSelectStepProps, SelectableUTXO } from '../types.js';
import { autoSelectGreedyUtxos, mapUnspentOutputsToSelectable } from '../../../../../utils/invitation-flow.js';
import type { UnspentOutputData } from '@xo-cash/state';

/** Default fee estimate in satoshis. */
const DEFAULT_FEE = 500n;

/** Dust threshold — outputs below this are unspendable. */
const DUST_THRESHOLD = 546n;

export function InputsSelectStep({
  invitation,
  appService,
  onComplete,
  onCancel,
  isActive,
}: InputsSelectStepProps): React.ReactElement {
  const [utxos, setUtxos] = useState<SelectableUTXO[]>([]);
  const [focusedIndex, setFocusedIndex] = useState(0);
  const [requiredAmount, setRequiredAmount] = useState(0n);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const { formatSatoshisToFiat } = useSatoshisConversion('USD');

  const fee = DEFAULT_FEE;

  // Derived totals
  const selectedAmount = utxos
    .filter(u => u.selected)
    .reduce((sum, u) => sum + u.valueSatoshis, 0n);
  const changeAmount = selectedAmount - requiredAmount - fee;
  const hasEnough = selectedAmount >= requiredAmount + fee;

  const getFiatSuffix = (satoshis: bigint): string => {
    const fiatValue = formatSatoshisToFiat(satoshis);
    return fiatValue ? ` (~${fiatValue})` : '';
  };

  /**
   * Determine the required satoshi amount from the invitation's variables.
   */
  const computeRequiredAmount = useCallback(async (): Promise<bigint> => {
    return await invitation.getSatsOut() ?? 0n;
  }, [invitation]);

  /**
   * Fetch suitable UTXOs from the engine and auto-select greedily.
   */
  const loadUtxos = useCallback(async () => {
    setIsLoading(true);
    setError(null);

    try {
      const required = await computeRequiredAmount();
      setRequiredAmount(required);

      const template = await appService.engine.getTemplate(invitation.data.templateIdentifier);
      if (!template) {
        throw new Error('Template not found');
      }

      // Get the action that we are calling from the template
      const action = template.actions[invitation.data.actionIdentifier];
      if (!action) {
        throw new Error('Action not found');
      }

      if (!action.transaction) {
        throw new Error('Action does not have a transaction');
      }

      // Get the transaction that the action is creating
      const transaction = template.transactions?.[action.transaction];
      if (!transaction) {
        throw new Error(`Transaction not found for action: ${action.transaction}`);
      }

      if (!transaction.outputs) {
        throw new Error(`Transaction does not have outputs`);
      }

      // Create a set to store all the output identifiers
      const outputIdentifiers = new Set<string>();
      for (const output of transaction.outputs) {
        outputIdentifiers.add(output.output);
      }

      // Create a map of the utxoID to suitable resource
      const utxoIdToSuitableResource = new Map<string, UnspentOutputData>();
      for (const outputIdentifier of outputIdentifiers) {
        const suitableResources = await invitation.findSuitableResources();
        for (const suitableResource of suitableResources) {
          utxoIdToSuitableResource.set(suitableResource.outpointTransactionHash + ':' + suitableResource.outpointIndex, suitableResource);
        }
      }

      const selectable = mapUnspentOutputsToSelectable(Array.from(utxoIdToSuitableResource.values()));
      const autoSelected = autoSelectGreedyUtxos(selectable, required + fee);
      setUtxos(autoSelected as SelectableUTXO[]);
    } catch (err) {
      setError(err instanceof Error ? err.message : String(err));
    } finally {
      setIsLoading(false);
    }
  }, [invitation, computeRequiredAmount, fee]);

  // Load UTXOs once on mount. We use a ref guard to prevent re-firing when
  // `loadUtxos` identity changes due to parent re-renders — each re-fire
  // flashes the loading state, causing the visible flicker bug.
  const hasLoadedRef = useRef(false);
  useEffect(() => {
    if (isActive && !hasLoadedRef.current) {
      hasLoadedRef.current = true;
      loadUtxos();
    }
  }, [isActive, loadUtxos]);

  /**
   * Toggle the selection of a UTXO at the given index.
   */
  const toggleSelection = useCallback((index: number) => {
    setUtxos(prev => {
      const updated = [...prev];
      const utxo = updated[index];
      if (utxo) updated[index] = { ...utxo, selected: !utxo.selected };
      return updated;
    });
  }, []);

  // Keyboard handling — gated by the import-flow layer so dialogs on top block input.
  useLayeredInput('import-flow', (input, key) => {
    if (key.upArrow || input === 'k') {
      setFocusedIndex(prev => Math.max(0, prev - 1));
    } else if (key.downArrow || input === 'j') {
      setFocusedIndex(prev => Math.min(utxos.length - 1, prev + 1));
    } else if (input === ' ') {
      if (utxos.length > 0) toggleSelection(focusedIndex);
    } else if (input === 'a') {
      setUtxos(prev => prev.map(u => ({ ...u, selected: true })));
    } else if (input === 'n') {
      setUtxos(prev => prev.map(u => ({ ...u, selected: false })));
    } else if (key.return) {
      if (hasEnough) {
        onComplete(utxos.filter(u => u.selected));
      }
    } else if (key.escape) {
      onCancel();
    }
  }, { isActive });

  // Loading state
  if (isLoading) {
    return (
      <Box flexDirection="column">
        <Text color={colors.info}>Finding suitable UTXOs...</Text>
      </Box>
    );
  }

  // Error state
  if (error) {
    return (
      <Box flexDirection="column">
        <Text color={colors.error} bold>Failed to load UTXOs</Text>
        <Text color={colors.textMuted}>{error}</Text>
        <Box marginTop={1}>
          <Text color={colors.textMuted}>Esc: Cancel</Text>
        </Box>
      </Box>
    );
  }

  // No UTXOs found
  if (utxos.length === 0) {
    return (
      <Box flexDirection="column">
        <Text color={colors.warning}>No suitable UTXOs found. Make sure your wallet has funds.</Text>
        <Box marginTop={1}>
          <Text color={colors.textMuted}>Esc: Cancel</Text>
        </Box>
      </Box>
    );
  }

  return (
    <Box flexDirection="column">
      {/* Summary bar */}
      <Box flexDirection="row" marginBottom={1}>
        <Text color={colors.primary} bold>Required: </Text>
        <Text color={colors.text}>
          {formatSatoshis(requiredAmount + fee)}
          {getFiatSuffix(requiredAmount + fee)}
        </Text>
        <Text color={colors.textMuted}> (amount {formatSatoshis(requiredAmount)} + fee {formatSatoshis(fee)})</Text>
      </Box>

      <Box flexDirection="row" marginBottom={1}>
        <Text color={colors.primary} bold>Selected: </Text>
        <Text color={hasEnough ? colors.success : colors.error}>
          {formatSatoshis(selectedAmount)}
          {getFiatSuffix(selectedAmount)}
        </Text>
        {hasEnough && changeAmount >= DUST_THRESHOLD && (
          <Text color={colors.textMuted}>
            {' '}
            (change: {formatSatoshis(changeAmount)}
            {getFiatSuffix(changeAmount)})
          </Text>
        )}
        {!hasEnough && (
          <Text color={colors.error}>
            {' '}
            — need {formatSatoshis(requiredAmount + fee - selectedAmount)}
            {getFiatSuffix(requiredAmount + fee - selectedAmount)} more
          </Text>
        )}
      </Box>

      {/* UTXO list */}
      <Text color={colors.primary} bold>UTXOs ({utxos.length}):</Text>
      {utxos.map((utxo, index) => {
        const isFocused = index === focusedIndex;
        const checkMark = utxo.selected ? '☑' : '☐';
        const txShort = utxo.outpointTransactionHash.slice(0, 8);

        return (
          <Box
            key={`${utxo.outpointTransactionHash}:${utxo.outpointIndex}`}
            flexDirection="column"
          >
            <Text
              color={isFocused ? colors.focus : utxo.selected ? colors.success : colors.text}
              bold={isFocused}
            >
              {isFocused ? '▸ ' : '  '}{checkMark} {formatSatoshis(utxo.valueSatoshis)} ({txShort}…:{utxo.outpointIndex})
            </Text>
            {formatSatoshisToFiat(utxo.valueSatoshis) && (
              <Text color={colors.textMuted}>
                {'   '}≈ {formatSatoshisToFiat(utxo.valueSatoshis)}
              </Text>
            )}
          </Box>
        );
      })}

      {/* Navigation hint */}
      <Box marginTop={1}>
        <Text color={colors.textMuted}>
          ↑↓: Navigate • Space: Toggle • a: All • n: None • return: Confirm • Esc: Cancel
        </Text>
      </Box>
    </Box>
  );
}
