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

import type {
  ApacuanaSDKProviderProps,
  ApacuanaSDKContextValue,
  ApacuanaSDKState,
  CertKeyInfo,
  UploadSignatureVariantData,
  SignDocumentParams,
} from '../types';

// Dependencias de terceros y utilidades
import apacuanaCore from 'apacuana-sdk-core';
import * as forge from 'node-forge';

import {
  generateCertificateMobile,
  generateKeyPairMobile,
} from '../utils/certificates';
import {
  encryptAndStoreValue,
  encryptData,
  getEncryptStoreValue,
  isPasswordCorrect,
  savePassword,
} from '../utils/crypto';
import { signDigest } from '../utils/signatures';
import type { GetDocsParams } from 'apacuana-sdk-core/dist/api/signatures';
import {
  addUserWithBiometrics,
  handleBiometricAuth,
  isBiometricEnabledForUser,
} from '../utils/biometric';
import { ApacuanaModal } from '../components/ApacuanaModal';
// import type { CertificateRequestParams } from 'apacuana-sdk-core/dist/types/certs';
import type { SignerData } from 'apacuana-sdk-core/dist/types/signatures';
import WebViewLiveness from '../components/WebViewLiveness';
import type { UserData } from 'apacuana-sdk-core/dist/types/users';

/**
 * Clave por defecto utilizada para la encriptación del CSR (Certificate Signing Request)
 * antes de ser enviado a la API.
 * @internal
 */
const keyDefault = 'dRgUkXp2s5v8y/B?';

/**
 * Contexto de React para el SDK de Apacuana.
 * Proporciona acceso al estado del SDK y a las funciones de interacción con la API.
 * Su valor es `undefined` por defecto y solo se define dentro de `ApacuanaProvider`.
 * @internal
 */
const ApacuanaContext = createContext<ApacuanaSDKContextValue | undefined>(
  undefined
);

/**
 * Componente Proveedor que gestiona el ciclo de vida y la configuración del SDK de Apacuana.
 * Debe envolver cualquier parte de la aplicación que necesite acceder a las funcionalidades del SDK.
 * @param {ApacuanaSDKProviderProps} props - Propiedades para configurar el proveedor,
 * incluyendo las credenciales del SDK y los componentes hijos.
 */
export const ApacuanaProvider = (props: ApacuanaSDKProviderProps) => {
  const { children, ...config } = props;

  /**
   * Estado interno del SDK que refleja su ciclo de vida.
   * - `idle`: Estado inicial.
   * - `initializing`: El SDK está en proceso de configuración.
   * - `ready`: El SDK está inicializado y listo para usarse.
   * - `error`: Ocurrió un error durante la inicialización.
   */
  const [state, setState] = useState<ApacuanaSDKState>({
    status: 'idle',
    error: null,
  });

  const [isModalVisible, setIsModalVisible] = useState(false);
  const [modalContent, setModalContent] = useState<React.ReactNode>(null);

  const openModal = useCallback((content: React.ReactNode) => {
    setModalContent(content);
    setIsModalVisible(true);
  }, []);

  const closeModal = useCallback(() => {
    setIsModalVisible(false);
    // Buena práctica: limpiar el contenido al cerrar para liberar memoria
    setModalContent(null);
  }, []);

  /**
   * Clave de encriptación utilizada para proteger los datos sensibles
   * (clave privada, certificados) en el almacenamiento seguro del dispositivo.
   */
  const encryptKey = config?.encryptionKey || '';

  /**
   * Devuelve la configuración actual con la que se inicializó el SDK Core.
   * @returns {object} El objeto de configuración del SDK.
   * @throws {Error} Si el SDK no está en estado 'ready'.
   */
  const getConfig = useCallback(() => {
    if (state.status !== 'ready') {
      throw new Error('El SDK no está listo. No se puede llamar a getConfig.');
    }
    return apacuanaCore.getConfig();
  }, [state.status]);

  /**
   * Obtiene la información completa del cliente.
   * @returns {Promise<GetCustomerResponse>} Una promesa que resuelve con los datos del cliente.
   * @throws {Error} Si el SDK no está en estado 'ready'.
   */
  const getCustomer = useCallback(async () => {
    if (state.status !== 'ready') {
      throw new Error(
        'El SDK no está listo. No se puede llamar a getCustomer.'
      );
    }
    return apacuanaCore.getCustomer();
  }, [state.status]);

  /**
   * Genera un certificado digital en el dispositivo, crea el CSR, lo envía a la API y almacena las credenciales.
   * @param {string} pin - PIN para proteger el certificado.
   * @param {boolean} enableSaveWithBiometrics - Si se debe habilitar biometría para guardar el usuario.
   * @returns {Promise<any>} Promesa con la respuesta de la API tras la emisión del certificado.
   * @throws {Error} Si el SDK no está listo o ocurre un error durante el proceso.
   */
  const generateCert = useCallback(
    async (pin: string, enableSaveWithBiometrics: boolean = false) => {
      if (state.status !== 'ready') {
        throw new Error(
          'El SDK no está listo. No se puede llamar a generateCertificate.'
        );
      }
      try {
        const sdkConfig = apacuanaCore.getConfig();
        const customerId = sdkConfig?.customerId;

        if (!pin) {
          return {
            success: false,
            message: 'Debe proporcionar un PIN para proteger el certificado.',
            code: 'ApacuanaSDKError',
          };
        }

        if (enableSaveWithBiometrics) {
          const isAuthenticated = await handleBiometricAuth();
          if (!isAuthenticated?.success) {
            return isAuthenticated;
          }
          await addUserWithBiometrics(customerId);
        }

        // 1. Generar par de claves (pública y privada) en el dispositivo.
        const keyPair = await generateKeyPairMobile();

        // 2. Generar una Solicitud de Firma de Certificado (CSR) usando el par de claves.
        const { csr } = (await generateCertificateMobile(keyPair)) as {
          csr: string;
        };

        // 3. Enviar el CSR encriptado a la API para su firma y emisión.
        const response: any = await apacuanaCore.generateCert({
          csr: encryptData(csr, keyDefault),
        } as any);

        if (!response?.success) {
          return response;
        }

        // 4. Almacenar de forma segura la clave privada y el certificado emitido.

        await encryptAndStoreValue(
          `apacuana-pk-${customerId}`,
          forge.util.encode64(keyPair.privateKey), // Se guarda en Base64 para consistencia.
          encryptKey
        );

        const keyCert = `apacuana-cert-${customerId}`;
        await encryptAndStoreValue(keyCert, response?.data?.cert, encryptKey);
        await encryptAndStoreValue(
          `apacuana-certId-${customerId}`,
          response?.data?.certifiedid,
          encryptKey
        );
        await savePassword(`apacuana-pin-${customerId}`, pin);

        // 5. Actualizar la lista local de certificados del usuario para referencia futura.
        const certKeysRaw = await getEncryptStoreValue(
          'apacuana-cert-keys',
          encryptKey
        );
        const updatedKeys: CertKeyInfo[] = certKeysRaw
          ? JSON.parse(certKeysRaw)
          : [];

        if (!updatedKeys.some((item) => item.keyCert === keyCert)) {
          const userData: any = sdkConfig?.userData;
          const newCertInfo: CertKeyInfo = {
            keyCert,
            name: `${userData?.name?.value} ${userData?.lastname?.value}`,
            email: userData?.usr,
            rifPrefix:
              userData?.rif?.kindrif?.toLowerCase() ||
              userData?.doc?.kinddoc?.toLowerCase() ||
              '',
            rif: userData?.rif,
            docPrefix: userData?.doc?.kinddoc?.toLowerCase() || '',
            doc: userData?.doc?.doc || '',
            idUser: userData?.id,
            typeCert: 'cert',
            certificationId: userData?.certificationId ?? '',
          };
          updatedKeys.push(newCertInfo);
          await encryptAndStoreValue(
            'apacuana-cert-keys',
            JSON.stringify(updatedKeys),
            encryptKey
          );
        }

        return response;
      } catch (error) {
        console.error(
          '[ApacuanaSDKError]: Error en generateCertificate:',
          error
        );
        return {
          success: false,
          message:
            (error as Error)?.message ||
            'Error desconocido durante la generación del certificado.',
          code: 'ApacuanaSDKError',
          error,
        };
      }
    },
    [encryptKey, state.status]
  );

  /**
   * Realiza una comprobación rápida si el certificado ya está almacenado en el dispositivo.
   * @returns {boolean} Promesa con la respuesta de la API tras la emisión del certificado.
   * @throws {Error} Si el SDK no está listo o ocurre un error durante el proceso.
   */
  const isCertificateInDevice = useCallback(async () => {
    if (state.status !== 'ready') {
      throw new Error(
        'El SDK no está listo. No se puede llamar a isCertificateInDevice.'
      );
    }
    const sdkConfig = apacuanaCore.getConfig();
    const customerId = sdkConfig?.customerId;
    const cert = await getEncryptStoreValue(
      `apacuana-cert-${customerId}`,
      encryptKey
    );
    return !!cert;
  }, [state.status, encryptKey]);

  /**
   * Consulta el estado actual del certificado del cliente.
   * @returns {Promise<GetCertStatusResponse>} Promesa con el estado del certificado.
   * @throws {Error} Si el SDK no está listo.
   */
  const getCertStatus = useCallback(async () => {
    if (state.status !== 'ready') {
      throw new Error(
        'El SDK no está listo. No se puede llamar a getCertStatus.'
      );
    }
    const sdkConfig = apacuanaCore.getConfig();
    const customerId = sdkConfig?.customerId;
    const cert = await getEncryptStoreValue(
      `apacuana-cert-${customerId}`,
      encryptKey
    );
    const isCertificateInDevice = !!cert;
    console.log('isCertificateInDevice:', isCertificateInDevice);
    return apacuanaCore.getCertStatus(isCertificateInDevice);
  }, [state.status, encryptKey]);

  /**
   * Obtiene los tipos de certificados disponibles para el cliente.
   * @returns {Promise<any>} Promesa con los tipos de certificados.
   * @throws {Error} Si el SDK no está listo.
   */
  const getCertTypes = useCallback(() => {
    if (state.status !== 'ready') {
      throw new Error(
        'El SDK no está listo. No se puede llamar a requestRevocation.'
      );
    }
    return apacuanaCore.getCertTypes();
  }, [state.status]);

  /**
   * Obtiene los requisitos para un tipo de usuario específico.
   * @param {Object} data - Objeto con el tipo de usuario.
   * @returns {Promise<any>} Promesa con los requisitos.
   * @throws {Error} Si el SDK no está listo.
   */
  const getRequerimentsByTypeUser = useCallback(
    (data: { type: number }) => {
      if (state.status !== 'ready') {
        throw new Error(
          'El SDK no está listo. No se puede llamar a requestRevocation.'
        );
      }
      return apacuanaCore.getRequerimentsByTypeUser(data);
    },
    [state.status]
  );

  /**
   * Agrega un firmante a un documento.
   * @param {SignerData} signerData - Datos del firmante a agregar.
   * @returns {Promise<any>} Promesa con la respuesta de la API.
   * @throws {Error} Si el SDK no está listo.
   */
  const addSigner = useCallback(
    async (signerData: SignerData) => {
      if (state.status !== 'ready') {
        throw new Error(
          'El SDK no está listo. No se puede llamar a addSigner.'
        );
      }
      try {
        const responseAddSigner = (await apacuanaCore.addSigner(
          signerData
        )) as any;
        if (!responseAddSigner?.success) {
          return responseAddSigner;
        }
        const docId = responseAddSigner?.data?.docId;
        const docs = await apacuanaCore.getDocs({ page: 0, size: 10 });
        const doc = docs?.data?.records[0] as any;
        return {
          success: true,
          data: {
            docId,
            doc,
            signature: doc?.signaturedata ?? {},
          },
          name: 'ApacuanaSuccess',
          statusCode: 200,
        };
      } catch (error) {
        return {
          success: false,
          message:
            (error as Error)?.message ||
            'Error desconocido al agregar firmante.',
          code: 'ApacuanaSDKError',
          error,
        };
      }
    },
    [state.status]
  );

  /**
   * Obtiene una lista paginada de documentos del cliente.
   * @param {GetDocsParams} data - Parámetros de paginación y filtrado.
   * @returns {Promise<GetDocsResponse>} Promesa con la lista de documentos.
   * @throws {Error} Si el SDK no está listo.
   */
  const getDocsByCustomer = useCallback(
    (data: GetDocsParams) => {
      if (state.status !== 'ready') {
        throw new Error('El SDK no está listo. No se puede llamar a getDocs.');
      }
      return apacuanaCore.getDocs(data);
    },
    [state.status]
  );

  /**
   * Orquesta el proceso de firma de un documento. La clave privada nunca abandona el dispositivo.
   * @param {SignDocumentParams} params - Datos de la firma, pin y biometría.
   * @returns {Promise<object>} Promesa con la respuesta de la API tras la firma.
   * @throws {Error} Si no se provee una firma, no se encuentran credenciales o falla la comunicación.
   */
  const signDocument = useCallback(
    async ({ signature, pin, enableBiometric }: SignDocumentParams) => {
      if (!signature) {
        // throw new Error('[ApacuanaSDKError]: Debe incluir una firma válida.');
        return {
          success: false,
          message: 'Debe incluir una firma válida.',
          code: 'ApacuanaSDKError',
        };
      }

      try {
        const sdkConfig = apacuanaCore.getConfig();
        const customerId = sdkConfig?.customerId;

        const isBiometricEnable = await isBiometricEnabledForUser(customerId);
        const isPasswordSuccess = await isPasswordCorrect(
          `apacuana-pin-${customerId}`,
          pin
        );

        if (isBiometricEnable?.success && enableBiometric) {
          const isAuthenticated = await handleBiometricAuth();
          if (!isAuthenticated?.success) {
            return isAuthenticated;
          }
        } else if (!isPasswordSuccess) {
          return {
            success: false,
            message: 'PIN incorrecto. No se puede proceder con la firma.',
            code: 'ApacuanaSDKError',
          };
        }

        // 1. Recuperar credenciales (certificado y clave privada) del almacenamiento seguro.
        const cert = await getEncryptStoreValue(
          `apacuana-cert-${customerId}`,
          encryptKey
        );
        if (!cert) {
          return {
            success: false,
            message:
              'No se encontró un certificado válido para el usuario. La firma no puede continuar.',
            code: 'ApacuanaSDKError',
          };
        }

        const privateKey = await getEncryptStoreValue(
          `apacuana-pk-${customerId}`,
          encryptKey
        );
        if (!privateKey) {
          return {
            success: false,
            message:
              'No se encontró una clave privada válida para el usuario. La firma no puede continuar.',
            code: 'ApacuanaSDKError',
          };
        }

        // 2. Obtener el 'digest' (hash) del documento a firmar desde la API.
        const digestBody = signature?.document
          ? {
              cert,
              signatureId: signature?.id,
              document: signature?.document,
            }
          : {
              cert,
              signatureId: signature?.id,
            };
        const digestData = await apacuanaCore.getDigest(digestBody);

        console.log('[digestData]:', digestData);

        if (!digestData?.data?.digest) {
          return digestData;
        }

        // 3. Firmar el digest localmente usando la clave privada del dispositivo.
        const signedDigest = await signDigest(
          digestData?.data?.digest,
          privateKey
        );

        const signBody = {
          signature: {
            id: signature?.id,
            positions: signature.positions,
          },
          cert,
          signedDigest,
          document: signature?.document ? signature?.document : undefined,
        };
        const response = await apacuanaCore.signDocument(signBody);
        return response;
      } catch (error) {
        return {
          success: false,
          message: 'Error al firmar el documento',
          code: 'ApacuanaSDKError',
          error: error,
        };
      }
    },
    [encryptKey]
  );

  /**
   * Sube una variante de firma (imagen) para un firmante específico.
   * Esta imagen se utilizará como la representación gráfica de la firma en los documentos.
   * @param {UploadSignatureVariantData} data - Datos de la imagen y firmante.
   * @returns {Promise<any>} Promesa con la respuesta de la API.
   * @throws {Error} Si el SDK no está listo.
   */
  const uploadSignatureVariant = useCallback(
    (data: UploadSignatureVariantData) => {
      if (state.status !== 'ready') {
        throw new Error(
          'El SDK no está listo. No se puede llamar a requestRevocation.'
        );
      }
      return apacuanaCore.uploadSignatureVariant(data);
    },
    [state.status]
  );

  /**
   * Obtiene la variante de firma (imagen) de un firmante.
   * @returns {Promise<any>} Promesa con la imagen de la firma.
   * @throws {Error} Si el SDK no está listo.
   */
  const getSignatureVariant = useCallback(() => {
    if (state.status !== 'ready') {
      throw new Error(
        'El SDK no está listo. No se puede llamar a requestRevocation.'
      );
    }
    return apacuanaCore.getSignatureVariant();
  }, [state.status]);

  /**
   * Elimina la variante de firma (imagen) de un firmante.
   * @returns {Promise<any>} Promesa con la respuesta de la API.
   * @throws {Error} Si el SDK no está listo.
   */
  const deleteSignatureVariant = useCallback(() => {
    if (state.status !== 'ready') {
      throw new Error(
        'El SDK no está listo. No se puede llamar a requestRevocation.'
      );
    }
    return apacuanaCore.deleteSignatureVariant();
  }, [state.status]);

  /**
   * Inicia el flujo de detección de fe de vida (liveness) facial y abre el modal correspondiente.
   * Genera un sessionId único y muestra el modal con la UI de liveness.
   * Retorna una Promesa que se resuelve cuando se dispara onComplete/onError/onCancel.
   */
  const startLivenessCheck = useCallback(() => {
    return new Promise(async (resolve) => {
      try {
        const response = await apacuanaCore.createFaceLivenessSession();
        const conf = apacuanaCore.getConfig();
        console.log('faceLiveness sessionId:', response?.data?.sessionId);

        if (!response?.success) {
          resolve(response);
          return;
        }

        let settled = false;
        const settle = (payload: any) => {
          if (settled) return;
          settled = true;
          // closeModal();
          resolve(payload);
          setTimeout(() => {
            closeModal();
          }, 1000);
        };

        openModal(
          <WebViewLiveness
            sessionId={response.data.sessionId}
            config={conf}
            onComplete={(payload?: any) => {
              console.log('FaceLivenessSuccess', payload);
              settle(
                payload ?? {
                  success: true,
                  message: 'Fe de vida completado con éxito.',
                }
              );
            }}
            onError={(e: any) => {
              console.log('FaceLivenessError', e);
              settle(
                e ?? {
                  success: false,
                  message: 'Error durante la verificación de liveness.',
                  code: 'LIVENESS_ERROR',
                }
              );
            }}
            onCancel={() => {
              console.log('FaceLivenessCanceled');
              settle({
                success: false,
                message: 'El usuario canceló la verificación de liveness.',
                code: 'LIVENESS_CANCELED',
              });
            }}
          />
        );
      } catch (error) {
        console.error('Error al iniciar la sesión de liveness:', error);
        resolve({
          success: false,
          message: 'Error al iniciar la sesión de liveness.',
          code: 'ApacuanaSDKError',
          error,
        });
      }
    });
  }, [openModal, closeModal]);

  /**
   * Valida el resultado de una sesión de prueba de vida.
   * @param {Object} data - Objeto con el sessionId de la sesión de liveness.
   * @returns {Promise<any>} Promesa con el resultado de la validación.
   * @throws {Error} Si el SDK no está listo.
   */
  const validateFaceLiveness = useCallback(
    (data: { sessionId: string }) => {
      if (state.status !== 'ready') {
        throw new Error(
          'El SDK no está listo. No se puede llamar a requestRevocation.'
        );
      }
      return apacuanaCore.validateFaceLiveness(data);
    },
    [state.status]
  );

  /**
   * Inicia una solicitud para revocar el certificado del cliente.
   * @param {number} reasonCode - Código numérico del motivo de la revocación.
   * @returns {Promise<RevocationResponse>} Promesa con la respuesta de la solicitud.
   * @throws {Error} Si el SDK no está listo.
   */
  const requestRevocation = useCallback(
    (reasonCode: number) => {
      if (state.status !== 'ready') {
        throw new Error(
          'El SDK no está listo. No se puede llamar a requestRevocation.'
        );
      }
      return apacuanaCore.requestRevocation({ reasonCode });
    },
    [state.status]
  );

  /**
   * Obtiene la lista de motivos de revocación de certificados.
   * @returns {Promise<RevocationReasonsResponse>} Promesa con la lista de motivos.
   * @throws {Error} Si el SDK no está listo.
   */
  const getRevocationReasons = useCallback(async () => {
    if (state.status !== 'ready') {
      throw new Error(
        'El SDK no está listo. No se puede llamar a getRevocationReasons.'
      );
    }
    return apacuanaCore.getRevocationReasons();
  }, [state.status]);

  /**
   * Crea un usuario Apacuana en la plataforma.
   * @param {UserData} userData - Datos del usuario a crear.
   * @returns {Promise<any>} Promesa con la respuesta de la API.
   * @throws {Error} Si el SDK no está listo.
   */
  const createApacuanaUser = useCallback(
    async (userData: UserData) => {
      if (state.status !== 'ready') {
        throw new Error(
          'El SDK no está listo. No se puede llamar a createApacuanaUser.'
        );
      }
      return apacuanaCore.createApacuanaUser(userData);
    },
    [state.status]
  );

  // /**
  //  * Solicitud de un certificado digital.
  //  * @param {CertificateRequestParams} params - Parámetros necesarios para la solicitud del certificado.
  //  * @returns {Promise<RequestCertificateResponse>} Una promesa que resuelve con la respuesta de la API.
  //  */
  // const requestCertificate = useCallback(
  //   (params: CertificateRequestParams) => {
  //     if (state.status !== 'ready') {
  //       throw new Error(
  //         'El SDK no está listo. No se puede llamar a requestRevocation.'
  //       );
  //     }
  //     console.log(params);
  //     return apacuanaCore.requestCertificate(params);
  //   },
  //   [state.status]
  // );

  /**
   * Hook de efecto que maneja la inicialización del SDK Core.
   * Se ejecuta cuando las propiedades de configuración clave cambian.
   */
  useEffect(() => {
    const initialize = async () => {
      setState(() => ({ status: 'initializing', error: null }));
      try {
        console.log('Iniciando SDK Core desde el Provider...', config);
        const initConfig = { ...config };
        const configToInit = initConfig?.customerId
          ? initConfig
          : (({ customerId: _customerId, ...rest }) => rest)(initConfig);
        if (!initConfig.customerId) {
          console.log(
            'No se proporcionó customerId. Se eliminará de la configuración de inicialización.'
          );
        }
        await apacuanaCore.init(configToInit);
        console.log('SDK Core inicializado correctamente desde el Provider');
        setState(() => ({ status: 'ready', error: null }));
      } catch (error) {
        console.error('Error al inicializar el SDK Core:', error);
        setState(() => ({ status: 'error', error: error as Error }));
        apacuanaCore.close();
      }
    };

    if (config.apiUrl && config.apiKey) {
      initialize();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [config.apiUrl, config.apiKey, config.secretKey, config.customerId]);

  const value: ApacuanaSDKContextValue = useMemo(
    () => ({
      state,
      getCustomer,
      getConfig,
      getDocsByCustomer,
      getCertStatus,
      getRevocationReasons,
      generateCert,
      requestRevocation,
      signDocument,
      startLivenessCheck,
      uploadSignatureVariant,
      getSignatureVariant,
      deleteSignatureVariant,
      getCertTypes,
      getRequerimentsByTypeUser,
      addSigner,
      // requestCertificate,
      validateFaceLiveness,
      isCertificateInDevice,
      createApacuanaUser,
    }),
    [
      state,
      getCustomer,
      getConfig,
      getDocsByCustomer,
      getCertStatus,
      getRevocationReasons,
      generateCert,
      requestRevocation,
      signDocument,
      startLivenessCheck,
      uploadSignatureVariant,
      getSignatureVariant,
      deleteSignatureVariant,
      getCertTypes,
      getRequerimentsByTypeUser,
      addSigner,
      // requestCertificate,
      validateFaceLiveness,
      isCertificateInDevice,
      createApacuanaUser,
    ]
  );

  return (
    <ApacuanaContext.Provider value={value}>
      {children}
      <ApacuanaModal isVisible={isModalVisible} onClose={closeModal}>
        {modalContent}
      </ApacuanaModal>
    </ApacuanaContext.Provider>
  );
};

/**
 * Hook personalizado para consumir el contexto del SDK de Apacuana.
 * Proporciona una forma sencilla y segura de acceder al estado y las funciones del SDK.
 * @returns {ApacuanaSDKContextValue} El valor del contexto, que incluye el estado y las funciones del SDK.
 * @throws {Error} Si el hook se utiliza fuera de un `ApacuanaProvider`.
 */
export const useApacuana = (): ApacuanaSDKContextValue => {
  const context = useContext(ApacuanaContext);
  // Esta validación asegura que el hook se use correctamente dentro del árbol de componentes.
  if (context === undefined) {
    throw new Error(
      'useApacuana debe ser utilizado dentro de un ApacuanaProvider'
    );
  }
  return context;
};
