import path from 'path';

import AuthStorage from '@arcblock/did-connect-storage-nedb';
// @ts-ignore
import { BlockletService } from '@blocklet/sdk/service/auth';
import { getWallet, getAccessWallet } from '@blocklet/sdk/lib/wallet';
import { WalletAuthenticator } from '@blocklet/sdk/lib/wallet-authenticator';
import { WalletHandlers } from '@blocklet/sdk/lib/wallet-handler';
import type { Request } from 'express';
import type { LiteralUnion } from 'type-fest';
import type { WalletObject } from '@ocap/wallet';

import env from './env';
import logger from './logger';

// Workaround #2: @blocklet/sdk's notification.getSender() uses
// `getWallet().address` (BLOCKLET_APP_SK derived) as the sender appDid for
// relay/EventBus broadcasts. On migrated blocklets that address (e.g. zNKti3…)
// is a rotating session id that the relay does not recognise, surfacing as:
//   "Sender blocklet does not exist: <addr>"
//   "Failed to broadcast event via relay: payment_intent.succeeded"
// The relay/EventBus calls in notification.js invoke `(0, exports.getSender)()`
// at send time, so mutating the module's own `exports.getSender` intercepts
// every call site. We must use `require()` (not `import default`) — the module
// sets `exports.default` to a subset object that does NOT include getSender,
// so the default-import alias cannot reach the live `exports.getSender`.
//
// Path must be `@blocklet/sdk/service/notification` (no `lib/`) — in Node it
// is a thin re-export that resolves via the module cache to the same object
// as `@blocklet/sdk/lib/service/notification`; in CF Workers the esbuild
// config aliases it to a no-op shim (patching a no-op is harmless). Using
// the `lib/` path breaks the CF build because it does not have an alias
// and falls through to `@blocklet/sdk` → `shims/blocklet-sdk/index.ts/lib/...`.
// Same root cause as the WalletAuthenticator override below — remove once the
// upstream sdk is patched.
// eslint-disable-next-line @typescript-eslint/no-var-requires, global-require, import/no-extraneous-dependencies
const notificationExports = require('@blocklet/sdk/service/notification');

notificationExports.getSender = () => ({
  appDid: process.env.BLOCKLET_APP_ID || getWallet(undefined, '', 'sk').address,
  wallet: getAccessWallet(),
});
logger.info('[sdk-patch] notification.getSender overridden', {
  appDid: notificationExports.getSender().appDid,
  expectedAppId: process.env.BLOCKLET_APP_ID,
});

export const wallet: WalletObject = getWallet();
export const ethWallet: WalletObject = getWallet('ethereum');

// Workaround for migrated blocklets where `BLOCKLET_APP_SK` is a rotating session
// key whose derived address ≠ appId. Upstream @blocklet/sdk's WalletAuthenticator
// passes `wallet: getWallet()` to did-connect-js, so outer.agentDid becomes that
// rotating address. Meanwhile the federated cert (signed by master) sets
// agentDid = `did:abt:${verifySite.appId}` (permanent), so the wallet-side strict
// check `cert.agentDid === outer.agentDid` fails with "Agent did does not match
// with certificate issuer."
//
// Force the authenticator to derive its signing wallet from BLOCKLET_APP_PK
// (permanent app pk → address = appId), aligning with the fix in
// @blocklet/sdk PR #12810 that already corrected getDelegatee. Remove once the
// upstream wallet-authenticator.ts is patched accordingly.
export const authenticator = new WalletAuthenticator({
  wallet: getWallet(undefined, '', 'sk'),
});
export const handlers = new WalletHandlers({
  authenticator,
  tokenStorage: new AuthStorage({
    dbPath: path.join(env.dataDir, 'auth.db'),
    // @ts-ignore
    onload: console.warn,
  }),
});

export const blocklet = new BlockletService();

export async function getVaultAddress() {
  try {
    const vault = await blocklet.getVault();
    if (!vault) {
      return null;
    }
    return vault;
  } catch (error) {
    logger.info('get vault wallet failed', { error });
    return null;
  }
}

export type CallbackArgs = {
  request: Request & { context: Record<string, any> };
  userDid: string;
  userPk: string;
  didwallet: {
    os: LiteralUnion<'ios' | 'android' | 'web', string>;
    version: string;
    jwt: string;
  };
  extraParams: Record<string, any>;
  updateSession: Function;
  claims: any[];
  step?: number;
  challenge?: string;
  pathname?: string;
  baseUrl?: string;
};
