/**
 * Wallet State Screen - Displays wallet balances and history.
 * 
 * Shows:
 * - Total balance
 * - Wallet history (invitations, reservations)
 * - Navigation to other actions
 */

import React, { useState, useEffect, useCallback, useMemo } from 'react';
import { Box, Text } from 'ink';
import { ScrollableList, type ListItemData } from '../components/List.js';
import { QRCode } from '../components/QRCode.js';
import { useNavigation } from '../hooks/useNavigation.js';
import { useAppContext, useStatus } from '../hooks/useAppContext.js';
import { useSatoshisConversion } from '../hooks/useSatoshisConversion.js';
import { useInputLayer, useLayeredInput, useBlockableInput, useIsInputCaptured } from '../hooks/useInputLayer.js';
import { colors, formatSatoshis, formatHex, logoSmall } from '../theme.js';
import type { HistoryItem } from '../../services/history.js';

import { generateTemplateIdentifier } from '@xo-cash/engine';
import { hexToBin, lockingBytecodeToCashAddress } from '@bitauth/libauth';

// Import utility functions
import {
  buildHistoryDisplayRows,
  getHistoryItemColorName,
  formatHistoryDate,
  type HistoryDisplayRow,
  type HistoryColorName,
} from '../../utils/history-utils.js';

/**
 * Map history color name to theme color.
 */
function getHistoryColor(colorName: HistoryColorName): string {
  switch (colorName) {
    case 'info':
      return colors.info as string;
    case 'warning':
      return colors.warning as string;
    case 'success':
      return colors.success as string;
    case 'error':
      return colors.error as string;
    case 'muted':
      return colors.textMuted as string;
    case 'text':
    default:
      return colors.text as string;
  }
}

/**
 * Menu action items.
 */
const menuItems: ListItemData<string>[] = [
  { key: 'new-tx', label: 'New Transaction (from template)', value: 'new-tx' },
  { key: 'import', label: 'Import Invitation', value: 'import' },
  { key: 'invitations', label: 'View Invitations', value: 'invitations' },
  { key: 'new-address', label: 'Generate New Address', value: 'new-address' },
  { key: 'unreserve-all', label: 'Unreserve All Resources', value: 'unreserve-all' },
  { key: 'refresh', label: 'Refresh', value: 'refresh' },
];

/**
 * History list item with display row value.
 */
type HistoryListItem = ListItemData<HistoryDisplayRow>;

/**
 * QR code dialog overlay — auto-captures input via the layer system.
 * Rendered only while a QR address is visible; closes on Enter/Esc.
 */
function QRDialogOverlay({ address, onClose }: { address: string; onClose: () => void }): React.ReactElement {
  useInputLayer('qr-dialog');

  useLayeredInput('qr-dialog', (_input, key) => {
    if (key.escape || key.return) {
      onClose();
    }
  });

  return (
    <Box
      position="absolute"
      flexDirection="column"
      alignItems="center"
      width="100%"
    >
      <QRCode
        value={address}
        dialog
        dialogTitle="Receive Address"
        showValue
      />
      <Box justifyContent="center" marginTop={1}>
        <Text color={colors.textMuted}>Press Enter or Esc to close</Text>
      </Box>
    </Box>
  );
}

/**
 * Wallet State Screen Component.
 * Displays wallet balance, history, and action menu.
 */
export function WalletStateScreen(): React.ReactElement {
  const { navigate } = useNavigation();
  const { appService, showError, showInfo } = useAppContext();
  const { setStatus } = useStatus();
  const {
    currencyCode,
    fiatPerBchRate,
    formattedFiatPerBchRate,
    formatSatoshisToFiat,
  } = useSatoshisConversion('USD');

  // State
  const [balance, setBalance] = useState<{ totalSatoshis: bigint; utxoCount: number } | null>(null);
  const [history, setHistory] = useState<HistoryItem[]>([]);
  const [focusedPanel, setFocusedPanel] = useState<'menu' | 'history'>('menu');
  const [selectedMenuIndex, setSelectedMenuIndex] = useState(0);
  const [selectedHistoryIndex, setSelectedHistoryIndex] = useState(0);
  const [isLoading, setIsLoading] = useState(true);

  /** Cash address to display in the QR code dialog (null when dialog is hidden). */
  const [qrAddress, setQrAddress] = useState<string | null>(null);

  /**
   * Refreshes wallet state.
   */
  const refresh = useCallback(async () => {
    if (!appService) {
      showError('AppService not initialized');
      return;
    }

    try {
      setIsLoading(true);
      setStatus('Loading wallet state...');

      // Get UTXOs for balance calculation
      const utxoData = await appService.engine.listUnspentOutputsData();

      // Calculate balance
      const selectableUtxos = utxoData.filter(utxo => utxo.selectable);
      const balanceData = selectableUtxos.reduce((acc, utxo) => acc + BigInt(utxo.valueSatoshis), BigInt(0));
      setBalance({
        totalSatoshis: balanceData,
        utxoCount: selectableUtxos.length,  
      });

      // Get wallet history from the history service
      const historyData = await appService.history.getHistory();
      setHistory(historyData);

      setStatus('Wallet ready');
      setIsLoading(false);
    } catch (error) {
      showError(`Failed to load wallet state: ${error instanceof Error ? error.message : String(error)}`);
      setIsLoading(false);
    }
  }, [appService, setStatus, showError]);

  // Load wallet state on mount
  useEffect(() => {
    refresh();
  }, [refresh]);

  // Keep wallet state in sync with invitation lifecycle and updates.
  useEffect(() => {
    if (!appService) return;

    const onWalletStateChanged = () => {
      void refresh();
    };

    appService.on('wallet-state-changed', onWalletStateChanged);

    return () => {
      appService.off('wallet-state-changed', onWalletStateChanged);
    };
  }, [appService, refresh]);

  /**
   * Generates a new receiving address and displays it as a QR code.
   */
  const generateNewAddress = useCallback(async () => {
    if (!appService) {
      showError('AppService not initialized');
      return;
    }

    try {
      setStatus('Generating new address...');

      // Get the default P2PKH template
      const templates = await appService.engine.listImportedTemplates();
      const p2pkhTemplate = templates.find(t => t.name?.includes('P2PKH'));

      if (!p2pkhTemplate) {
        showError('P2PKH template not found');
        return;
      }

      // Generate the template identifier
      const templateId = generateTemplateIdentifier(p2pkhTemplate);

      // Generate the locking bytecode (returned as a hex string)
      const lockingBytecodeHex = await appService.engine.generateLockingBytecode(
        templateId,
        'receiveOutput',
        'receiver',
      );

      // Convert the locking bytecode to a BCH cash address for display and QR encoding.
      const result = lockingBytecodeToCashAddress({ bytecode: hexToBin(lockingBytecodeHex), prefix: 'bitcoincash' });

      if (typeof result === 'string') {
        showError(`Failed to encode address: ${result}`);
        return;
      }

      console.log(result);

      setQrAddress(result.address);
      setStatus('Address generated');

      // Refresh to show updated state
      await refresh();
    } catch (error) {
      showError(`Failed to generate address: ${error instanceof Error ? error.message : String(error)}`);
    }
  }, [appService, setStatus, showError, refresh]);

  /**
   * Unreserves all reserved UTXOs and refreshes the wallet state.
   */
  const unreserveAll = useCallback(async () => {
    if (!appService) {
      showError('AppService not initialized');
      return;
    }

    try {
      setStatus('Unreserving all resources...');
      const count = await appService.unreserveAllResources();
      showInfo(`Unreserved ${count} resource(s)`);
      await refresh();
    } catch (error) {
      showError(`Failed to unreserve resources: ${error instanceof Error ? error.message : String(error)}`);
    }
  }, [appService, setStatus, showError, showInfo, refresh]);

  /**
   * Handles menu action.
   */
  const handleMenuAction = useCallback((action: string) => {
    switch (action) {
      case 'new-tx':
        navigate('templates');
        break;
      case 'import':
        navigate('invitations', { mode: 'import' });
        break;
      case 'invitations':
        navigate('invitations', { mode: 'list' });
        break;
      case 'new-address':
        generateNewAddress();
        break;
      case 'unreserve-all':
        unreserveAll();
        break;
      case 'refresh':
        refresh();
        break;
    }
  }, [navigate, generateNewAddress, unreserveAll, refresh]);

  /**
   * Handle menu item activation.
   */
  const handleMenuItemActivate = useCallback((item: ListItemData<string>, index: number) => {
    if (item.value) {
      handleMenuAction(item.value);
    }
  }, [handleMenuAction]);

  /**
   * Build history list items for ScrollableList.
   */
  const historyListItems = useMemo((): HistoryListItem[] => {
    return buildHistoryDisplayRows(history).map(row => {
      return {
        key: row.id,
        label: row.label,
        description: row.description,
        value: row,
        color: getHistoryItemColorName(row, false),
        hidden: false,
      };
    });
  }, [history]);

  /**
   * Fiat values are memoized so we only recompute when balance or rate changes.
   */
  const formattedUsdPerBchRate = useMemo(() => {
    return formattedFiatPerBchRate;
  }, [formattedFiatPerBchRate]);

  const formattedUsdBalance = useMemo(() => {
    if (!balance || fiatPerBchRate === null) {
      return null;
    }

    return formatSatoshisToFiat(balance.totalSatoshis);
  }, [balance, fiatPerBchRate, formatSatoshisToFiat]);

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

  // Screen input — automatically blocked when any dialog/overlay is capturing.
  const isCaptured = useIsInputCaptured();

  useBlockableInput((_input, key) => {
    if (key.tab) {
      setFocusedPanel(prev => prev === 'menu' ? 'history' : 'menu');
    }
  });

  /**
   * Render custom history list item.
   */
  const renderHistoryItem = useCallback((
    item: HistoryListItem,
    isSelected: boolean,
    isFocused: boolean
  ): React.ReactNode => {
    const row = item.value;
    if (!row) return null;

    const colorName = getHistoryItemColorName(row, isFocused);
    const itemColor = isFocused ? colors.focus : getHistoryColor(colorName);
    const dateStr = formatHistoryDate(row.timestamp);
    const indicator = isFocused ? '▸ ' : '  ';
    const groupingPrefix = row.isNested ? '  -> ' : '';

    if (row.type === 'invitation') {
      return (
        <Box flexDirection="row" justifyContent="space-between">
          <Text color={itemColor}>
            {indicator}[Invitation] {row.label}
          </Text>
          {dateStr && <Text color={colors.textMuted}>{dateStr}</Text>}
        </Box>
      );
    }

    if (row.type === 'invitation_input') {
      const inputSatoshis = row.utxo?.valueSatoshis;
      const inputFiatSuffix = inputSatoshis !== undefined
        ? getFiatSuffix(inputSatoshis)
        : '';
      return (
        <Box flexDirection="row" justifyContent="space-between">
          <Box>
            <Text color={itemColor}>
              {indicator}{groupingPrefix}[Input] {row.label}
              {inputFiatSuffix}
            </Text>
            {row.description && <Text color={colors.textMuted}> {row.description}</Text>}
          </Box>
          {dateStr && <Text color={colors.textMuted}>{dateStr}</Text>}
        </Box>
      );
    }

    if (row.type === 'invitation_output') {
      const sats = row.utxo?.valueSatoshis ?? 0n;
      return (
        <Box flexDirection="row" justifyContent="space-between">
          <Box flexDirection="row">
            <Text color={itemColor}>
              {indicator}{groupingPrefix}[Output] {formatSatoshis(sats)}
              {getFiatSuffix(sats)}
            </Text>
            {row.description && <Text color={colors.textMuted}> {row.description}</Text>}
          </Box>
          {dateStr && <Text color={colors.textMuted}>{dateStr}</Text>}
        </Box>
      );
    }

    if (row.type === 'utxo') {
      const sats = row.utxo?.valueSatoshis ?? 0n;
      const reservedTag = row.utxo?.reserved ? ' [Reserved]' : '';
      return (
        <Box flexDirection="row" justifyContent="space-between">
          <Box flexDirection="row">
            <Text color={itemColor}>
              {indicator}{formatSatoshis(sats)}
              {getFiatSuffix(sats)}
            </Text>
            {row.description && <Text color={colors.textMuted}> {row.description}{reservedTag}</Text>}
          </Box>
          {dateStr && <Text color={colors.textMuted}>{dateStr}</Text>}
        </Box>
      );
    }

    // Fallback for other types
    return (
      <Box flexDirection="row" justifyContent="space-between">
        <Text color={itemColor}>
          {indicator}{row.label}
        </Text>
        {dateStr && <Text color={colors.textMuted}>{dateStr}</Text>}
      </Box>
    );
  }, [getFiatSuffix]);

  return (
    <Box flexDirection="column" flexGrow={1}>
      {/* Header */}
      <Box borderStyle="single" borderColor={colors.secondary} paddingX={1}>
        <Text color={colors.primary} bold>{logoSmall} - Wallet Overview</Text>
      </Box>

      {/* Main content */}
      <Box flexDirection="row" marginTop={1} flexGrow={1}>
        {/* Left column: Balance */}
        <Box 
          flexDirection="column" 
          width="50%" 
          paddingRight={1}
        >
          <Box
            borderStyle="single"
            borderColor={colors.primary}
            flexDirection="column"
            paddingX={1}
            paddingY={1}
          >
            <Text color={colors.primary} bold> Balance </Text>
            <Box marginTop={1} flexDirection="column">
              <Text color={colors.text}>Total Balance:</Text>
              {balance ? (
                <>
                  <Text color={colors.success} bold>
                    {formatSatoshis(balance.totalSatoshis)}
                  </Text>
                  {formattedUsdBalance ? (
                    <Text color={colors.info}>
                      Approx. Fiat ({currencyCode}): {formattedUsdBalance}
                    </Text>
                  ) : (
                    <Text color={colors.textMuted}>
                      Approx. Fiat ({currencyCode}): Waiting for BCH/{currencyCode} rate...
                    </Text>
                  )}
                  {formattedUsdPerBchRate && (
                    <Text color={colors.textMuted}>
                      1 BCH = {formattedUsdPerBchRate}
                    </Text>
                  )}
                  <Text color={colors.textMuted}>
                    UTXOs: {balance.utxoCount}
                  </Text>
                </>
              ) : (
                <Text color={colors.textMuted}>Loading...</Text>
              )}
            </Box>
          </Box>
        </Box>

        {/* Right column: Actions menu */}
        <Box 
          flexDirection="column" 
          width="50%" 
          paddingLeft={1}
        >
          <Box
            borderStyle="single"
            borderColor={focusedPanel === 'menu' ? colors.focus : colors.border}
            flexDirection="column"
            paddingX={1}
          >
            <Text color={colors.primary} bold> Actions </Text>
            <ScrollableList
              items={menuItems}
              selectedIndex={selectedMenuIndex}
              onSelect={setSelectedMenuIndex}
              onActivate={handleMenuItemActivate}
              focus={focusedPanel === 'menu' && !isCaptured}
              emptyMessage="No actions"
            />
          </Box>
        </Box>
      </Box>

      {/* Wallet History */}
      <Box marginTop={1} flexGrow={1}>
        <Box
          borderStyle="single"
          borderColor={focusedPanel === 'history' ? colors.focus : colors.border}
          flexDirection="column"
          paddingX={1}
          width="100%"
          height={14}
          overflow="hidden"
        >
          <Text color={colors.primary} bold> Wallet History {history.length > 0 ? `(${selectedHistoryIndex + 1}/${history.length})` : ''}</Text>
          {isLoading ? (
            <Box marginTop={1}>
              <Text color={colors.textMuted}>Loading...</Text>
            </Box>
          ) : (
            <ScrollableList
              items={historyListItems}
              selectedIndex={selectedHistoryIndex}
              onSelect={setSelectedHistoryIndex}
              focus={focusedPanel === 'history' && !isCaptured}
              maxVisible={10}
              emptyMessage="No history found"
              renderItem={renderHistoryItem}
            />
          )}
        </Box>
      </Box>

      {/* Help text */}
      <Box marginTop={1}>
        <Text color={colors.textMuted} dimColor>
          Tab: Switch focus • Enter: Select • ↑↓: Navigate • Esc: Back
        </Text>
      </Box>

      {/* QR Code dialog overlay for generated addresses */}
      {qrAddress && (
        <QRDialogOverlay
          address={qrAddress}
          onClose={() => setQrAddress(null)}
        />
      )}
    </Box>
  );
}
