/**
 * InvitationImportFlow — orchestrates the multi-step invitation import.
 *
 * Manages the step state machine, accumulates data from each step, and
 * injects it into the next step via props (dependency injection).
 *
 * Supports two display modes:
 * - `'dialog'`: renders as an absolute-positioned overlay (used when called from InvitationScreen)
 * - `'screen'`: renders as a full-screen component with header, step indicator, and button bar
 */

import React, { useState, useCallback } from 'react';
import { Box, Text } from 'ink';
import { colors, logoSmall } from '../../../theme.js';
import { StepIndicator, type Step } from '../../../components/ProgressBar.js';

import { FetchInvitationStep } from './steps/FetchInvitationStep.js';
import { PreviewInvitationStep } from './steps/PreviewInvitationStep.js';
import { RoleSelectStep } from './steps/RoleSelectStep.js';
import { InputsSelectStep } from './steps/InputsSelectStep.js';
import { ReviewStep } from './steps/ReviewStep.js';

import { IMPORT_STEPS, type ImportFlowProps, type SelectableUTXO } from './types.js';
import type { Invitation } from '../../../../services/invitation.js';
import type { XOTemplate } from '@xo-cash/types';
import { DialogWrapper } from '../../../components/Dialog.js';
import { useInputLayer, useLayeredInput } from '../../../hooks/useInputLayer.js';
import { InvitationBuilder } from '@xo-cash/engine';
import { hexToBin } from '@bitauth/libauth';

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

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

export function InvitationImportFlow({
  invitationId,
  mode,
  appService,
  onClose,
  showError,
  showInfo,
  setStatus,
}: ImportFlowProps): React.ReactElement {
  // ── Accumulated state ────────────────────────────────────────────────────
  const [currentStep, setCurrentStep] = useState(0);
  const [invitation, setInvitation] = useState<Invitation | null>(null);
  const [buildableInvitation, setBuildableInvitation] = useState<InvitationBuilder | null>(null);
  const [template, setTemplate] = useState<XOTemplate | null>(null);
  const [availableRoles, setAvailableRoles] = useState<string[]>([]);
  const [selectedRole, setSelectedRole] = useState<string | null>(null);
  const [selectedInputs, setSelectedInputs] = useState<SelectableUTXO[]>([]);
  const [changeAmount, setChangeAmount] = useState(0n);
  const [requiredAmount, setRequiredAmount] = useState(0n);

  // ── Cancel handler ───────────────────────────────────────────────────────
  /**
   * Cleans up (removes the invitation if it was fetched) and signals the parent.
   */
  const handleCancel = useCallback(async () => {
    if (invitation && appService) {
      try {
        await appService.removeInvitation(invitation);
      } catch {
        // Best-effort removal — don't block close on failure
      }
    }
    onClose();
  }, [invitation, appService, onClose]);

  // ── Step completion callbacks ────────────────────────────────────────────

  /**
   * FetchStep completed — invitation and template are now available.
   * Also pre-fetches available roles for the next steps.
   */
  const handleFetchComplete = useCallback(async (inv: Invitation, tmpl: XOTemplate | null) => {
    setInvitation(inv);
    setTemplate(tmpl);

    const builder = InvitationBuilder.fromInvitation(inv.data);
    setBuildableInvitation(builder);

    try {
      const roles = await inv.getAvailableRoles();
      setAvailableRoles(roles);
    } catch (err) {
      showError(`Failed to get roles: ${err instanceof Error ? err.message : String(err)}`);
    }

    setCurrentStep(1); // → Preview
  }, [showError]);

  /** PreviewStep completed — user reviewed the invitation state and wants to proceed. */
  const handlePreviewComplete = useCallback(() => {
    setCurrentStep(2); // → Role Select
  }, []);

  /** RoleSelectStep completed — user picked a role. */
  const handleRoleComplete = useCallback((role: string) => {
    setSelectedRole(role);
    setCurrentStep(3); // → Inputs Select
  }, []);

  /** InputsSelectStep completed — user selected UTXOs. */
  const handleInputsComplete = useCallback(async (inputs: SelectableUTXO[]) => {
    setSelectedInputs(inputs);

    await invitation?.addInputs(inputs.map(input => ({
      outpointTransactionHash: hexToBin(input.outpointTransactionHash),
      outpointIndex: input.outpointIndex,
    })));

    // Compute totals from selected inputs
    const totalSelected = inputs.reduce((sum, u) => sum + u.valueSatoshis, 0n);

    // Determine required amount from invitation variables
    const requiredSats = await invitation?.getSatsOut() ?? 0n;
    setRequiredAmount(requiredSats);

    // Set the change amount for the review step
    const changeAmountSats = totalSelected - requiredSats - DEFAULT_FEE;
    setChangeAmount(changeAmountSats);

    // Add the change output if it exceeds the dust threshold
    if (changeAmountSats >= DUST_THRESHOLD) {
      await invitation?.addOutputs([{
        valueSatoshis: changeAmountSats,
      }]);
    }

    setCurrentStep(4); // → Review
  }, [invitation, buildableInvitation, selectedInputs]);

  /** ReviewStep completed — invitation import is done. */
  const handleReviewComplete = useCallback(() => {
    const roleName = (() => {
      if (!selectedRole || !template) return selectedRole ?? '';
      const raw = template.roles?.[selectedRole];
      return (raw && typeof raw === 'object' && 'name' in raw) ? String(raw.name) : selectedRole;
    })();

    showInfo(
      `Invitation imported and accepted!\n\n` +
      `Role: ${roleName}\n` +
      `Template: ${template?.name ?? invitation?.data.templateIdentifier ?? 'Unknown'}\n` +
      `Action: ${invitation?.data.actionIdentifier ?? 'Unknown'}`
    );
    setStatus('Ready');
    onClose();
  }, [selectedRole, template, invitation, showInfo, setStatus, onClose]);

  // ── Keyboard handling ────────────────────────────────────────────────────
  // The import flow registers its own layer so it captures input above the
  // parent screen.  Individual steps also register sub-layers when needed.
  useInputLayer('import-flow');

  useLayeredInput('import-flow', (_input, key) => {
    if (currentStep !== 0) return;
    // Enter retries, Esc cancels — handled within FetchStep rendering,
    // but we also catch Esc here for safety.
    if (key.escape) handleCancel();
  });

  // ── Step router ──────────────────────────────────────────────────────────
  const renderStep = (): React.ReactNode => {
    const stepDef = IMPORT_STEPS[currentStep];
    if (!stepDef) return null;

    switch (stepDef.type) {
      case 'fetch':
        return (
          <FetchInvitationStep
            invitationId={invitationId}
            appService={appService}
            onComplete={handleFetchComplete}
            onCancel={handleCancel}
            isActive={true}
          />
        );

      case 'preview':
        if (!invitation) return null;
        return (
          <PreviewInvitationStep
            invitation={invitation}
            template={template}
            onComplete={handlePreviewComplete}
            onCancel={handleCancel}
            isActive={true}
          />
        );

      case 'role-select':
        if (!invitation) return null;
        return (
          <RoleSelectStep
            invitation={invitation}
            template={template}
            availableRoles={availableRoles}
            onComplete={handleRoleComplete}
            onCancel={handleCancel}
            isActive={true}
          />
        );

      case 'inputs-select':
        if (!invitation || !selectedRole) return null;
        return (
          <InputsSelectStep
            invitation={invitation}
            template={template}
            selectedRole={selectedRole}
            appService={appService}
            onComplete={handleInputsComplete}
            onCancel={handleCancel}
            isActive={true}
          />
        );

      case 'review':
        if (!invitation || !selectedRole) return null;
        return (
          <ReviewStep
            invitation={invitation}
            template={template}
            selectedRole={selectedRole}
            selectedInputs={selectedInputs}
            changeAmount={changeAmount}
            requiredAmount={requiredAmount}
            appService={appService}
            onComplete={handleReviewComplete}
            onCancel={handleCancel}
            isActive={true}
          />
        );

      default:
        return null;
    }
  };

  // ── Step indicator data ──────────────────────────────────────────────────
  const indicatorSteps: Step[] = IMPORT_STEPS.map(s => ({ label: s.name }));

  // ── Layout: dialog mode ──────────────────────────────────────────────────
  if (mode === 'dialog') {
    return (
      <Box
          position="absolute"
          flexDirection="column"
          alignItems="center"
          justifyContent="center"
          width="100%"
          height="100%"
        >
        <DialogWrapper title="Import Invitation" borderColor={colors.primary}>
          {/* Step indicator (compact) */}
          <Box marginTop={1}>
            <StepIndicator steps={indicatorSteps} currentStep={currentStep} />
          </Box>

          {/* Step content */}
          <Box marginTop={1} flexDirection="column">
            {renderStep()}
          </Box>
        </DialogWrapper>
      </Box>
    );
  }

  // ── Layout: screen mode ──────────────────────────────────────────────────
  return (
    <Box flexDirection="column" flexGrow={1}>
      {/* Header */}
      <Box borderStyle="single" borderColor={colors.secondary} paddingX={1} flexDirection="column">
        <Text color={colors.primary} bold>{logoSmall} - Import Invitation</Text>
        <Text color={colors.textMuted}>
          {template?.name ?? 'Loading...'}
          {selectedRole ? ` (as ${selectedRole})` : ''}
        </Text>
      </Box>

      {/* Step indicator */}
      <Box marginTop={1} paddingX={1}>
        <StepIndicator steps={indicatorSteps} currentStep={currentStep} />
      </Box>

      {/* Step content */}
      <Box
        borderStyle="single"
        borderColor={colors.primary}
        flexDirection="column"
        paddingX={1}
        paddingY={1}
        marginTop={1}
        marginX={1}
        flexGrow={1}
      >
        <Text color={colors.primary} bold>
          {IMPORT_STEPS[currentStep]?.name ?? 'Unknown'} ({currentStep + 1}/{IMPORT_STEPS.length})
        </Text>
        <Box marginTop={1} flexDirection="column">
          {renderStep()}
        </Box>
      </Box>

      {/* Help text */}
      <Box marginTop={1} marginX={1}>
        <Text color={colors.textMuted} dimColor>
          Esc: Cancel import
        </Text>
      </Box>
    </Box>
  );
}
