/**
 * MIT License
 *
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

import { EventEmitter, TurboModule } from '@rnoh/react-native-openharmony/ts';
import { TM } from './generated/ts';
import { common } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { wxopensdk, WXEventHandler } from '../../../ts';
import { http } from '@kit.NetworkKit';
import { fileIo, fileUri } from '@kit.CoreFileKit';
import { Want } from '@kit.AbilityKit';
import { bundleManager } from '@kit.AbilityKit';
import OpenLinkOptions from '@ohos.app.ability.OpenLinkOptions';

enum LoadingStatus {
  INIT,
  LOADING,
  SCANNING,
  SCANNED,
  SUCCESS,
  FAILED,
  INVALID_APP_SECRET,
}

type CallbackBoolean = (error: string | null, result: null | boolean) => void

interface GeneratedTypeLiteralInterface_2 {
  authCode: string | null;
  errCode: wxopensdk.OAuthErrCode | null;
}

type CallbackAuthCode = (error: string | null, result: GeneratedTypeLiteralInterface_2 | null) => void

export class WechatLibTurboModule extends TurboModule implements TM.WechatLibTurboModule.Spec {
  private appId: string | null = null;
  private wxEventHandler = WXEventHandler
  private static wxApi: wxopensdk.WXApi | null = null;
  private context = getContext(this) as common.UIAbilityContext;
  lastOAuth: wxopensdk.IDiffDevOAuth | null = null
  loadStatus: LoadingStatus = LoadingStatus.INIT
  private NOT_REGISTERED = "registerApp required.";
  private API_NOT_SUPPORTED = "Api not supported.";
  private INVOKE_FAILED = "WeChat API invoke returns false.";
  private INVALID_ARGUMENT = "invalid argument.";
  private logger = this.ctx.logger.clone('WechatLibTurboModuleLogger');
  listener = new EventEmitter();
  unsubscribeAuthGotQrcode = () => {
  };

  subscribeAuthGotQrcode(onQRGet: (qrcode: string) => void) {
    this.unsubscribeAuthGotQrcode = this.listener.subscribe(
      'onAuthGotQrcode',
      (qrcode: string) => onQRGet && onQRGet(qrcode),
    );
  }

  unSubscribeAuthGotQrcode() {
    this.unsubscribeAuthGotQrcode();
  }

  registerCallback(name: string, handler: (result: null | Object) => void) {
    this.wxEventHandler.registerOnWXRespCallback(name, handler)
  }

  unregisterCallback(name: string) {
    this.wxEventHandler.unregisterOnWXRespCallback(name)
  }

  public static handleWant(want: Want) {
    WechatLibTurboModule.wxApi?.handleWant(want, WXEventHandler)
  }

  /**
   * 暂不支持的接口统一返回
   * @returns
   */
  notSupportPromise(callback: CallbackBoolean) {
    if (!WechatLibTurboModule.wxApi) {
      callback(this.NOT_REGISTERED, null);
    } else {
      callback(this.API_NOT_SUPPORTED, null);
    }
  }

  /**
   * 注册应用，初始化WechatLibTurboModule.wxApi
   * @param appId
   * @param universalLink
   * @returns
   */
  registerApp(appId: string, universalLink: string, callback: CallbackBoolean) {
    try {
      this.appId = appId;
      WechatLibTurboModule.wxApi = wxopensdk.WXAPIFactory.createWXAPI(appId);
      callback(null, true)
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Unknown error';
      this.logger.error(`Error while registering app with appId ${appId}: ${message}`);
      callback(message, false)
    }
  }

  isWXAppInstalled(callback: CallbackBoolean): void {
    if (!WechatLibTurboModule.wxApi) {
      callback(this.NOT_REGISTERED, null);
      return;
    }
    try {
      let link = 'weixin://test.example.com';
      let canOpen = bundleManager.canOpenLink(link);
      this.logger.info(JSON.stringify(canOpen));
      callback(null, canOpen);
    } catch (err) {
      let message = (err as BusinessError).message;
      callback(message, null);
    }
  }

  isWXAppSupportApi(callback: CallbackBoolean): void {
    return this.notSupportPromise(callback)
  }

  getApiVersion(callback: CallbackBoolean): void {
    return this.notSupportPromise(callback)
  }

  /**
   * 打开微信
   * @returns
   */
  async openWXApp(callback: CallbackBoolean) {
    if (!WechatLibTurboModule.wxApi) {
      callback(this.NOT_REGISTERED, null);
      return;
    }

    try {
      const res = await WechatLibTurboModule.wxApi.openWechat(this.context) as boolean;
      this.logger.info('res:' + res)
      callback(null, res);
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Unknown error';
      this.logger.error(`Error while openWXAppopenWXApp ${message}`);
    }
  }

  async sendAuthRequest(scope: string, state: string, callback: CallbackBoolean) {
    if (!WechatLibTurboModule.wxApi) {
      callback(this.NOT_REGISTERED, null);
      return;
    }

    let req = new wxopensdk.SendAuthReq
    req.isOption1 = false
    req.nonAutomatic = true
    req.scope = scope
    req.state = state
    req.transaction = ''

    try {
      const res = await WechatLibTurboModule.wxApi.sendReq(this.context, req) as boolean;
      this.logger.info('sendAuthRequest res:' + res)
      callback(null, res);
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Unknown error';
      this.logger.error(`Error while sendAuthRequest ${message}`);
    }
  }

  // 触发 QRCode 事件
  emitQRCode(base64String: string) {
    this.listener.emit('onAuthGotQrcode', base64String);
  }

  async authByScan(
    appid: string,
    nonceStr: string,
    timeStamp: string,
    scope: string,
    signature: string,
    schemeData: string,
    callback: CallbackAuthCode
  ) {
    return callback(this.API_NOT_SUPPORTED, null);
  }

  async shareText(message: TM.WechatLibTurboModule.ShareTextMetadata, callback: CallbackBoolean) {
    if (!WechatLibTurboModule.wxApi) {
      callback(this.NOT_REGISTERED, null)
      return
    }

    // 初始化 WXTextObject 对象
    let textObject = new wxopensdk.WXTextObject
    textObject.text = message.text
    // 用 WXTextObject 对象初始化 WXMediaMessage 对象
    let mediaMessage = new wxopensdk.WXMediaMessage()
    mediaMessage.mediaObject = textObject

    // 创建 SendMessageToWX.Req 对象
    let req = new wxopensdk.SendMessageToWXReq()
    req.scene = message.scene ?? wxopensdk.SendMessageToWXReq.WXSceneSession
    req.message = mediaMessage

    try {
      // 调用 api.sendReq 方法并返回结果
      const res = await WechatLibTurboModule.wxApi.sendReq(this.context, req) as boolean;
      callback(null, res);
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Unknown error';
      this.logger.error(`Error while openWXAppopenWXApp ${message}`);
    }
  }

  downloadImageWithUrl(url: string,
    completion: (imageData: ArrayBuffer) => void) {
    http.createHttp().request(url,
      (error: BusinessError, data: http.HttpResponse) => {
        if (error || data.responseCode != 200) {
          return
        }
        completion((data.result as ArrayBuffer))
      })
  }

  async shareImage(message: TM.WechatLibTurboModule.ShareImageMetadata, callback: CallbackBoolean) {
    if (!WechatLibTurboModule.wxApi) {
      callback(this.NOT_REGISTERED, null);
      return;
    }

    this.downloadImageWithUrl(message.imageUrl, async (imageData: ArrayBuffer) => {
      if (!imageData) {
        callback("Failed to retrieve imageData", null);
        return;
      }

      try {
        let file: fileIo.File | undefined;
        let filePath = getContext(this).filesDir + `/original-${Date.now()}.jpg`;
        file = fileIo.openSync(filePath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE);
        fileIo.writeSync(file.fd, imageData);
        fileIo.closeSync(file);

        // 初始化 WXImageObject
        let imageObject = new wxopensdk.WXImageObject();
        imageObject.uri = fileUri.getUriFromPath(filePath);

        // 初始化 WXMediaMessage 对象
        let mediaMessage = new wxopensdk.WXMediaMessage();
        mediaMessage.mediaObject = imageObject;

        let req = new wxopensdk.SendMessageToWXReq();
        req.scene = message.scene ?? wxopensdk.SendMessageToWXReq.WXSceneSession;
        req.message = mediaMessage;

        if (!WechatLibTurboModule.wxApi) {
          callback(this.NOT_REGISTERED, null);
          return;
        }

        const res = await WechatLibTurboModule.wxApi.sendReq(this.context, req) as boolean;
        callback(null, res);
      } catch (err) {
        const message = err instanceof Error ? err.message : 'downloadImageWithUrl Unknown error';
        callback(message, null);
      }
    });

  }

  async shareLocalImage(message: TM.WechatLibTurboModule.ShareImageMetadata, callback: CallbackBoolean) {
    if (!WechatLibTurboModule.wxApi) {
      callback(this.NOT_REGISTERED, null);
      return;
    }

    try {
      let imageObject = new wxopensdk.WXImageObject();
      // 需要用fileUri.getUriFromPath处理成带有包名的路径才能分享出去
      imageObject.uri = fileUri.getUriFromPath(message.imageUrl.replace("file://", ""))

      let mediaMessage = new wxopensdk.WXMediaMessage()
      mediaMessage.mediaObject = imageObject
      let req = new wxopensdk.SendMessageToWXReq()
      req.scene = message.scene ?? wxopensdk.SendMessageToWXReq.WXSceneSession;
      req.message = mediaMessage

      const res = await WechatLibTurboModule.wxApi.sendReq(this.context, req) as boolean;
      callback(null, res);
    } catch (err) {
      const message = err instanceof Error ? err.message : 'shareLocalImage Unknown error';
      callback(message, null);
    }
  }

  shareFile(message: TM.WechatLibTurboModule.ShareFileMetadata, callback: CallbackBoolean): void {
    return this.notSupportPromise(callback)
  }

  shareMusic(message: TM.WechatLibTurboModule.ShareMusicMetadata, callback: CallbackBoolean): void {
    return this.notSupportPromise(callback)
  }

  shareVideo(message: TM.WechatLibTurboModule.ShareVideoMetadata, callback: CallbackBoolean): void {
    return this.notSupportPromise(callback)
  }

  shareWebpage(message: TM.WechatLibTurboModule.ShareWebpageMetadata, callback: CallbackBoolean): void {
    return this.notSupportPromise(callback)
  }

  shareMiniProgram(message: TM.WechatLibTurboModule.ShareMiniProgramMetadata, callback: CallbackBoolean): void {
    return this.notSupportPromise(callback)
  }

  async launchMiniProgram(message: TM.WechatLibTurboModule.LaunchMiniProgramMetadata, callback: CallbackBoolean) {
    if (!WechatLibTurboModule.wxApi) {
      callback(this.NOT_REGISTERED, null);
      return;
    }
    let miniReq = new wxopensdk.LaunchMiniProgramReq
    // 小程序原始id
    miniReq.userName = message.userName
    //拉起小程序页面的可带参路径，不填默认拉起小程序首页
    miniReq.path = message.path
    // 可选打开 开发版，体验版和正式版
    miniReq.miniprogramType = message.miniProgramType

    try {
      this.logger.info('launchMiniProgram miniReq:' + JSON.stringify(miniReq))
      const res = await WechatLibTurboModule.wxApi.sendReq(this.context, miniReq) as boolean;
      this.logger.info('launchMiniProgram res:' + res)
      if (!res) {
        return callback(this.INVALID_ARGUMENT, null);
      }
    } catch (err) {
      const message = err instanceof Error ? err.message : 'Unknown error';
      callback(message, null);
      this.logger.error(`Error while sendAuthRequest ${message}`);
    }
  }

  chooseInvoice(data: TM.WechatLibTurboModule.ChooseInvoice, callback: CallbackBoolean): void {
    return this.notSupportPromise(callback);
  }

  async pay(payload: TM.WechatLibTurboModule.PaymentLoad, callback: (result: Object | null) => void) {
    if (!WechatLibTurboModule.wxApi) {
      throw new Error(this.NOT_REGISTERED);
    }
    let payReq = new wxopensdk.PayReq
    if (payload.partnerId) {
      payReq.partnerId = payload.partnerId;
    }
    if (this.appId) {
      payReq.appId = this.appId;
    }
    if (payload.prepayId) {
      payReq.prepayId = payload.prepayId;
    }
    if (payload.nonceStr) {
      payReq.nonceStr = payload.nonceStr;
    }
    if (payload.timeStamp) {
      payReq.timeStamp = payload.timeStamp;
    }
    if (payload.sign) {
      payReq.sign = payload.sign;
    }
    if (payload.package) {
      payReq.packageValue = payload.package;
    }
    if (payload.extData) {
      payReq.extData = payload.extData;
    }

    try {
      const res = await WechatLibTurboModule.wxApi.sendReq(this.context, payReq) as boolean;
      callback(res ? null : this.INVOKE_FAILED);
    } catch (err) {
      const message = err instanceof Error ? err.message : 'shareLocalImage Unknown error';
      callback(message);
    }
  }

  subscribeMessage(message: TM.WechatLibTurboModule.SubscribeMessageMetadata, callback: CallbackBoolean): void {
    return this.notSupportPromise(callback)
  }
}
