import type React from 'react';

import type {
  AvailableUiKit,
  Connector,
  ContractAdapter,
  ContractFunction,
  ContractSchema,
  EcosystemReactUiProviderProps,
  EcosystemSpecificReactHooks,
  EcosystemWalletComponents,
  ExecutionConfig,
  ExecutionMethodDetail,
  FieldType,
  FormFieldType,
  FunctionParameter,
  NativeConfigLoader,
  RelayerDetails,
  RelayerDetailsRich,
  StellarNetworkConfig,
  TransactionStatusUpdate,
  TxStatus,
  UiKitConfiguration,
  UserRpcProviderConfig,
  WalletConnectionStatus,
} from '@openzeppelin/contracts-ui-builder-types';
import { isStellarNetworkConfig } from '@openzeppelin/contracts-ui-builder-types';
import { logger } from '@openzeppelin/contracts-ui-builder-utils';

// Import functions from modules
import { loadStellarContract, loadStellarContractWithMetadata } from './contract/loader';
import { StellarRelayerOptions } from './transaction/components';
import { RelayerExecutionStrategy } from './transaction/relayer';

import {
  getStellarExplorerAddressUrl,
  getStellarExplorerTxUrl,
  getStellarSupportedExecutionMethods,
  testStellarRpcConnection,
  validateStellarExecutionConfig,
  validateStellarRpcEndpoint,
} from './configuration';
import {
  generateStellarDefaultField,
  getStellarCompatibleFieldTypes,
  mapStellarParameterTypeToFieldType,
} from './mapping';
import {
  getStellarWritableFunctions,
  isStellarViewFunction,
  queryStellarViewFunction,
} from './query';
import { formatStellarTransactionData, signAndBroadcastStellarTransaction } from './transaction';
import { formatStellarFunctionResult } from './transform';
import { validateAndConvertStellarArtifacts } from './utils';
import { isValidAddress as isStellarValidAddress, type StellarAddressType } from './validation';
import {
  connectStellarWallet,
  disconnectStellarWallet,
  generateStellarWalletsKitExportables,
  getInitializedStellarWalletImplementation,
  getResolvedWalletComponents,
  getStellarAvailableConnectors,
  getStellarWalletImplementation,
  loadInitialConfigFromAppService,
  resolveFullUiKitConfiguration,
  stellarFacadeHooks,
  stellarUiKitManager,
  StellarWalletConnectionStatus,
  StellarWalletUiRoot,
  supportsStellarWalletConnection,
} from './wallet';

/**
 * Stellar-specific adapter implementation using explicit method delegation.
 *
 * NOTE: Contains placeholder implementations for most functionalities.
 */
export class StellarAdapter implements ContractAdapter {
  readonly networkConfig: StellarNetworkConfig;
  readonly initialAppServiceKitName: UiKitConfiguration['kitName'];

  constructor(networkConfig: StellarNetworkConfig) {
    if (!isStellarNetworkConfig(networkConfig)) {
      throw new Error('StellarAdapter requires a valid Stellar network configuration.');
    }
    this.networkConfig = networkConfig;

    // Set the network config in the wallet UI kit manager
    stellarUiKitManager.setNetworkConfig(networkConfig);

    // Initialize wallet implementation with network config
    getStellarWalletImplementation(networkConfig).catch((error) => {
      logger.error(
        'StellarAdapter:constructor',
        'Failed to initialize wallet implementation:',
        error
      );
    });

    // Determine the initial kitName from AppConfigService at the time of adapter construction.
    // This provides a baseline kitName preference from the application's static/global configuration.
    // It defaults to 'custom' if no specific kitName is found in AppConfigService.
    const initialGlobalConfig = loadInitialConfigFromAppService();
    this.initialAppServiceKitName =
      (initialGlobalConfig.kitName as UiKitConfiguration['kitName']) || 'custom';

    logger.info(
      'StellarAdapter:constructor',
      'Initial kitName from AppConfigService noted:',
      this.initialAppServiceKitName
    );

    logger.info(
      'StellarAdapter',
      `Adapter initialized for network: ${networkConfig.name} (ID: ${networkConfig.id})`
    );
  }

  // --- Contract Loading --- //
  /**
   * NOTE about artifact inputs (single input with auto-detection):
   *
   * The Builder renders the contract definition step using whatever fields the
   * adapter returns here. EVM uses one optional ABI field; Midnight provides
   * multiple fields. Stellar should use a single input approach with automatic
   * content detection when we add manual-spec support:
   *
   * - Keep `contractAddress` (required)
   * - Add optional `contractDefinition` (type: `code-editor`, language: `json`)
   *   with file upload support for both JSON and Wasm binary content
   *
   * When this field is added:
   * - Extend `validateAndConvertStellarArtifacts(...)` to accept
   *   `{ contractAddress, contractDefinition? }`
   * - In the loader, branch: if `contractDefinition` provided, auto-detect
   *   content type (JSON vs Wasm using magic bytes `\0asm`):
   *   - For JSON: Parse and validate as Soroban spec, use `transformStellarSpecToSchema`
   *   - For Wasm: Extract embedded spec from binary, parse locally (no RPC)
   * - Set `source: 'manual'` with `contractDefinitionOriginal` to the raw
   *   user-provided content. This ensures auto-save captures and restores the
   *   manual contract definition exactly like the EVM/Midnight flows.
   * - Provide clear UI hints about supported formats (JSON spec or Wasm binary).
   */
  public getContractDefinitionInputs(): FormFieldType[] {
    return [
      {
        id: 'contractAddress',
        name: 'contractAddress',
        label: 'Contract ID',
        type: 'blockchain-address',
        validation: { required: true },
        placeholder: 'C...',
        helperText: 'Enter the Stellar contract ID (C...).',
      },
    ];
  }

  /**
   * @inheritdoc
   */
  public async loadContract(source: string | Record<string, unknown>): Promise<ContractSchema> {
    // Convert generic input to Stellar-specific artifacts
    const artifacts = validateAndConvertStellarArtifacts(source);
    const result = await loadStellarContract(artifacts, this.networkConfig);
    return result.schema;
  }

  /**
   * @inheritdoc
   */
  public async loadContractWithMetadata(source: string | Record<string, unknown>): Promise<{
    schema: ContractSchema;
    source: 'fetched' | 'manual';
    contractDefinitionOriginal?: string;
    metadata?: {
      fetchedFrom?: string;
      contractName?: string;
      fetchTimestamp?: Date;
      definitionHash?: string;
    };
  }> {
    try {
      // Convert generic input to Stellar-specific artifacts
      const artifacts = validateAndConvertStellarArtifacts(source);
      const result = await loadStellarContractWithMetadata(artifacts, this.networkConfig);

      return {
        schema: result.schema,
        source: result.source,
        contractDefinitionOriginal: result.contractDefinitionOriginal,
        metadata: result.metadata,
      };
    } catch (error) {
      // Re-throw errors consistently with EVM adapter
      throw error;
    }
  }

  getWritableFunctions(contractSchema: ContractSchema): ContractSchema['functions'] {
    return getStellarWritableFunctions(contractSchema);
  }

  // --- Type Mapping & Field Generation --- //
  mapParameterTypeToFieldType(parameterType: string): FieldType {
    return mapStellarParameterTypeToFieldType(parameterType);
  }
  getCompatibleFieldTypes(parameterType: string): FieldType[] {
    return getStellarCompatibleFieldTypes(parameterType);
  }
  generateDefaultField<T extends FieldType = FieldType>(
    parameter: FunctionParameter,
    contractSchema?: ContractSchema
  ): FormFieldType<T> {
    return generateStellarDefaultField(parameter, contractSchema);
  }

  // --- Transaction Formatting & Execution --- //
  public formatTransactionData(
    contractSchema: ContractSchema,
    functionId: string,
    submittedInputs: Record<string, unknown>,
    fields: FormFieldType[]
  ): unknown {
    return formatStellarTransactionData(contractSchema, functionId, submittedInputs, fields);
  }
  async signAndBroadcast(
    transactionData: unknown,
    executionConfig: ExecutionConfig,
    onStatusChange: (status: TxStatus, details: TransactionStatusUpdate) => void,
    runtimeApiKey?: string
  ): Promise<{ txHash: string }> {
    return signAndBroadcastStellarTransaction(
      transactionData,
      executionConfig,
      this.networkConfig,
      onStatusChange,
      runtimeApiKey
    );
  }

  // NOTE: waitForTransactionConfirmation? is optional in the interface.
  // Since the imported function is currently undefined, we omit the method here.
  // If implemented in ./transaction/sender.ts later, add the method back:
  // async waitForTransactionConfirmation?(...) { ... }

  // --- View Function Querying --- //
  isViewFunction(functionDetails: ContractFunction): boolean {
    return isStellarViewFunction(functionDetails);
  }

  // Implement queryViewFunction with the correct signature from ContractAdapter
  async queryViewFunction(
    contractAddress: string,
    functionId: string,
    params: unknown[] = [],
    contractSchema?: ContractSchema
  ): Promise<unknown> {
    return queryStellarViewFunction(
      contractAddress,
      functionId,
      this.networkConfig,
      params,
      contractSchema,
      (address: string) => this.loadContract({ contractAddress: address })
    );
  }

  formatFunctionResult(decodedValue: unknown, functionDetails: ContractFunction): string {
    return formatStellarFunctionResult(decodedValue, functionDetails);
  }

  // --- Wallet Interaction --- //
  supportsWalletConnection(): boolean {
    return supportsStellarWalletConnection();
  }
  async getAvailableConnectors(): Promise<Connector[]> {
    return getStellarAvailableConnectors();
  }
  async connectWallet(
    connectorId: string
  ): Promise<{ connected: boolean; address?: string; error?: string }> {
    return connectStellarWallet(connectorId);
  }
  async disconnectWallet(): Promise<{ disconnected: boolean; error?: string }> {
    return disconnectStellarWallet();
  }
  getWalletConnectionStatus(): StellarWalletConnectionStatus {
    const impl = getInitializedStellarWalletImplementation();
    if (!impl) {
      return {
        isConnected: false,
        address: undefined,
        chainId: stellarUiKitManager.getState().networkConfig?.id || 'stellar-testnet',
      };
    }

    const stellarStatus = impl.getWalletConnectionStatus();

    // Return the rich Stellar-specific status directly
    return stellarStatus;
  }

  /**
   * @inheritdoc
   */
  onWalletConnectionChange(
    callback: (
      currentStatus: WalletConnectionStatus,
      previousStatus: WalletConnectionStatus
    ) => void
  ): () => void {
    const walletImplementation = getInitializedStellarWalletImplementation();
    if (!walletImplementation) {
      logger.warn(
        'StellarAdapter:onWalletConnectionChange',
        'Wallet implementation not ready. Subscription may not work.'
      );
      return () => {};
    }

    return walletImplementation.onWalletConnectionChange(
      (currentImplStatus, previousImplStatus) => {
        callback(currentImplStatus, previousImplStatus);
      }
    );
  }

  // --- Configuration & Metadata --- //
  async getSupportedExecutionMethods(): Promise<ExecutionMethodDetail[]> {
    return getStellarSupportedExecutionMethods();
  }
  async validateExecutionConfig(config: ExecutionConfig): Promise<true | string> {
    const walletStatus = this.getWalletConnectionStatus();
    return validateStellarExecutionConfig(config, walletStatus);
  }

  // Implement getExplorerUrl with the correct signature from ContractAdapter
  getExplorerUrl(address: string): string | null {
    return getStellarExplorerAddressUrl(address, this.networkConfig);
  }

  // Implement getExplorerTxUrl with the correct signature from ContractAdapter
  getExplorerTxUrl?(txHash: string): string | null {
    if (getStellarExplorerTxUrl) {
      return getStellarExplorerTxUrl(txHash, this.networkConfig);
    }
    return null;
  }

  // --- Validation --- //
  isValidAddress(address: string, addressType?: string): boolean {
    return isStellarValidAddress(address, addressType as StellarAddressType);
  }

  public async getAvailableUiKits(): Promise<AvailableUiKit[]> {
    return [
      {
        id: 'custom',
        name: 'Stellar Wallets Kit Custom',
        configFields: [],
      },
      {
        id: 'stellar-wallets-kit',
        name: 'Stellar Wallets Kit',
        configFields: [],
      },
    ];
  }

  /**
   * @inheritdoc
   */
  public async configureUiKit(
    programmaticOverrides: Partial<UiKitConfiguration> = {},
    options?: {
      loadUiKitNativeConfig?: NativeConfigLoader;
    }
  ): Promise<void> {
    const currentAppServiceConfig = loadInitialConfigFromAppService();

    // Delegate the entire configuration resolution to the service function
    const finalFullConfig = await resolveFullUiKitConfiguration(
      programmaticOverrides,
      this.initialAppServiceKitName,
      currentAppServiceConfig,
      options
    );

    // Configure the Stellar UI kit manager
    await stellarUiKitManager.configure(finalFullConfig);
    logger.info(
      'StellarAdapter:configureUiKit',
      'StellarUiKitManager configuration requested with final config:',
      finalFullConfig
    );
  }

  /**
   * @inheritdoc
   */
  public async getExportableWalletConfigFiles(
    uiKitConfig?: UiKitConfiguration
  ): Promise<Record<string, string>> {
    if (uiKitConfig?.kitName === 'stellar-wallets-kit') {
      return generateStellarWalletsKitExportables(uiKitConfig);
    }
    return {};
  }

  /**
   * @inheritdoc
   */
  public getEcosystemWalletComponents(): EcosystemWalletComponents | undefined {
    const currentManagerState = stellarUiKitManager.getState();

    // Only attempt to resolve components if the manager has a configuration set
    if (!currentManagerState.currentFullUiKitConfig) {
      logger.debug(
        'StellarAdapter:getEcosystemWalletComponents',
        'No UI kit configuration available in manager yet. Returning undefined components.'
      );
      return undefined;
    }

    // Use the service to resolve components based on the current UI kit configuration
    const components = getResolvedWalletComponents(currentManagerState.currentFullUiKitConfig);
    return components;
  }

  /**
   * @inheritdoc
   */
  public getEcosystemReactUiContextProvider():
    | React.ComponentType<EcosystemReactUiProviderProps>
    | undefined {
    logger.info(
      'StellarAdapter:getEcosystemReactUiContextProvider',
      'Returning StellarWalletUiRoot.'
    );
    return StellarWalletUiRoot;
  }

  /**
   * @inheritdoc
   */
  public getEcosystemReactHooks(): EcosystemSpecificReactHooks | undefined {
    // Always provide hooks for Stellar adapter regardless of UI kit
    return stellarFacadeHooks;
  }

  public async getRelayers(serviceUrl: string, accessToken: string): Promise<RelayerDetails[]> {
    const relayerStrategy = new RelayerExecutionStrategy();
    try {
      return await relayerStrategy.getStellarRelayers(serviceUrl, accessToken, this.networkConfig);
    } catch (error) {
      logger.error('StellarAdapter', 'Failed to fetch Stellar relayers:', error);
      return Promise.resolve([]);
    }
  }

  public async getRelayer(
    serviceUrl: string,
    accessToken: string,
    relayerId: string
  ): Promise<RelayerDetailsRich> {
    const relayerStrategy = new RelayerExecutionStrategy();
    try {
      return await relayerStrategy.getStellarRelayer(
        serviceUrl,
        accessToken,
        relayerId,
        this.networkConfig
      );
    } catch (error) {
      logger.error('StellarAdapter', 'Failed to fetch Stellar relayer details:', error);
      return Promise.resolve({} as RelayerDetailsRich);
    }
  }

  /**
   * Returns a React component for configuring Stellar-specific relayer transaction options.
   * @returns The Stellar relayer options component
   */
  public getRelayerOptionsComponent():
    | React.ComponentType<{
        options: Record<string, unknown>;
        onChange: (options: Record<string, unknown>) => void;
      }>
    | undefined {
    return StellarRelayerOptions;
  }

  /**
   * @inheritdoc
   */
  public async validateRpcEndpoint(rpcConfig: UserRpcProviderConfig): Promise<boolean> {
    // TODO: Implement Stellar-specific RPC validation when needed
    return validateStellarRpcEndpoint(rpcConfig);
  }

  /**
   * @inheritdoc
   */
  public async testRpcConnection(rpcConfig: UserRpcProviderConfig): Promise<{
    success: boolean;
    latency?: number;
    error?: string;
  }> {
    // TODO: Implement Stellar-specific RPC validation when needed
    return testStellarRpcConnection(rpcConfig);
  }

  /**
   * @inheritdoc
   */
  public getUiLabels(): Record<string, string> | undefined {
    return {
      relayerConfigTitle: 'Transaction Configuration',
      relayerConfigActiveDesc: 'Customize transaction parameters for submission',
      relayerConfigInactiveDesc: 'Using recommended transaction configuration for reliability',
      relayerConfigPresetTitle: 'Recommended Preset Active',
      relayerConfigPresetDesc: 'Transactions will use recommended parameters for quick inclusion',
      relayerConfigCustomizeBtn: 'Customize Settings',
      detailsTitle: 'Relayer Details',
      network: 'Network',
      relayerId: 'Relayer ID',
      active: 'Active',
      paused: 'Paused',
      systemDisabled: 'System Disabled',
      balance: 'Balance',
      // For Stellar, sequence number is conceptually similar; adapters supply the value
      nonce: 'Sequence',
      pending: 'Pending Transactions',
      lastTransaction: 'Last Transaction',
    };
  }
}

// Also export as default to ensure compatibility with various import styles
export default StellarAdapter;
