// Copyright 2019-2026 @pezkuwi/extension-ui authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { AccountJson, RequestSign } from '@pezkuwi/extension-base/background/types';
import type { ExtrinsicPayload } from '@pezkuwi/types/interfaces';
import type { SignerPayloadJSON, SignerPayloadRaw } from '@pezkuwi/types/types';
import type { HexString } from '@pezkuwi/util/types';

import React, { useCallback, useContext, useEffect, useState } from 'react';

import { TypeRegistry } from '@pezkuwi/types';

import { ActionContext, Address, VerticalSpace, Warning } from '../../../components/index.js';
import { useMetadata, useTranslation } from '../../../hooks/index.js';
import { approveSignSignature } from '../../../messaging.js';
import Bytes from '../Bytes.js';
import Extrinsic from '../Extrinsic.js';
import LedgerSign from '../LedgerSign.js';
import Qr from '../Qr.js';
import SignArea from './SignArea.js';

interface Props {
  account: AccountJson;
  buttonText: string;
  isFirst: boolean;
  request: RequestSign;
  signId: string;
  url: string;
}

interface Data {
  hexBytes: string | null;
  payload: ExtrinsicPayload | null;
}

export const CMD_MORTAL = 2;
export const CMD_SIGN_MESSAGE = 3;

// keep it global, we can and will re-use this across requests
const registry = new TypeRegistry();

function isRawPayload (payload: SignerPayloadJSON | SignerPayloadRaw): payload is SignerPayloadRaw {
  return !!(payload as SignerPayloadRaw).data;
}

export default function Request ({ account: { accountIndex, addressOffset, genesisHash, isExternal, isHardware, type }, buttonText, isFirst, request, signId, url }: Props): React.ReactElement<Props> | null {
  const onAction = useContext(ActionContext);
  const [{ hexBytes, payload }, setData] = useState<Data>({ hexBytes: null, payload: null });
  const [error, setError] = useState<string | null>(null);
  const { t } = useTranslation();
  const chain = useMetadata(genesisHash);

  useEffect((): void => {
    // When the chain and request are ready, configure the chain's registry.
    // This will be picked up by LedgerSign.
    if (chain && !isRawPayload(request.payload)) {
      chain.registry.setSignedExtensions(request.payload.signedExtensions, chain.definition.userExtensions);
    }
  }, [chain, request]);

  useEffect((): void => {
    const payload = request.payload;

    if (isRawPayload(payload)) {
      setData({
        hexBytes: payload.data,
        payload: null
      });
    } else {
      registry.setSignedExtensions(payload.signedExtensions);

      setData({
        hexBytes: null,
        payload: registry.createType('ExtrinsicPayload', payload, { version: payload.version })
      });
    }
  }, [request]);

  const _onSignature = useCallback(
    ({ signature }: { signature: HexString }, signedTransaction?: HexString): void => {
      approveSignSignature(signId, signature, signedTransaction)
        .then(() => onAction())
        .catch((error: Error): void => {
          setError(error.message);
          console.error(error);
        });
    },
    [onAction, signId]
  );

  if (payload !== null) {
    const json = request.payload as SignerPayloadJSON;

    return (
      <>
        <div>
          <Address
            address={json.address}
            genesisHash={json.genesisHash}
            isExternal={isExternal}
            isHardware={isHardware}
          />
        </div>
        {isExternal && !isHardware
          ? (
            <Qr
              address={json.address}
              cmd={CMD_MORTAL}
              genesisHash={json.genesisHash}
              onSignature={_onSignature}
              payload={payload}
            />
          )
          : (
            <Extrinsic
              payload={payload}
              request={json}
              url={url}
            />
          )
        }
        {isHardware && (
          <LedgerSign
            accountIndex={accountIndex || 0}
            addressOffset={addressOffset || 0}
            error={error}
            genesisHash={json.genesisHash}
            isEthereum={type === 'ethereum'}
            onSignature={_onSignature}
            payloadExt={payload}
            payloadJson={json}
            setError={setError}
          />
        )}
        <SignArea
          buttonText={buttonText}
          error={error}
          isExternal={isExternal}
          isFirst={isFirst}
          setError={setError}
          signId={signId}
        />
      </>
    );
  } else if (hexBytes !== null) {
    const { address, data } = request.payload as SignerPayloadRaw;

    return (
      <>
        <div>
          <Address
            address={address}
            isExternal={isExternal}
          />
        </div>
        {isExternal && !isHardware && genesisHash
          ? (
            <Qr
              address={address}
              cmd={CMD_SIGN_MESSAGE}
              genesisHash={genesisHash}
              onSignature={_onSignature}
              payload={data}
            />
          )
          : (
            <Bytes
              bytes={data}
              url={url}
            />
          )
        }
        <VerticalSpace />
        {isExternal && !isHardware && !genesisHash && (
          <>
            <Warning isDanger>{t('"Allow use on any network" is not supported to show a QR code. You must associate this account with a network.')}</Warning>
            <VerticalSpace />
          </>
        )}
        {isHardware && <>
          <Warning>{t('Message signing is not supported for hardware wallets.')}</Warning>
          <VerticalSpace />
        </>}
        <SignArea
          buttonText={buttonText}
          error={error}
          isExternal={isExternal}
          isFirst={isFirst}
          setError={setError}
          signId={signId}
        />
      </>
    );
  }

  return null;
}
