/*
 * Copyright (c) Huawei Device Co., Ltd. 2024-2025. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import HashSet from '@ohos.util.HashSet';
import { LogDomain, LogHelper } from '@ohos/basicutils';
import { CommonUtil } from '../utils/CommonUtil';

const TAG = 'InterceptorManager';
const log: LogHelper = LogHelper.getLogHelper(LogDomain.KG, TAG);
export type ObserverCallback<T> = (args?: T) => void;

export interface ILog {
  getLogTag(): string;
}

export enum ObserverStageEnum {
  INITIAL = 'INITIAL',
  BEFORE = 'BEFORE',
  PROCESSING = 'PROCESSING',
  AFTER = 'AFTER',
}

export abstract class AbstractObserver<T> implements ILog {
  private subscribers: Map<string, HashSet<ObserverCallback<T>>> = new Map();

  public getLogTag(): string {
    return this.constructor?.name || 'AbstractObserver';
  }

  public getSubscriberNames(): IterableIterator<string> {
    this.infoLog(`subscribers size=${this.subscribers?.size}`);
    return this.subscribers.keys();
  }

  public getSubscriberSize(key: string): number {
    const count: number | undefined = this.subscribers?.get(key)?.length;
    if (count === undefined) {
      return 0;
    }
    return count;
  }

  protected infoLog(msg: string): void {
    log.info(this.getLogTag(), msg);
  }

  protected debugLog(msg: string): void {
    log.debug(this.getLogTag(), msg);
  }

  protected needNotify(): boolean {
    return this.subscribers.size !== 0;
  }

  protected publishAtStage(stage: string, data?: T): void {
    this.debugLog(`start to publish at stage ${stage}`);
    CommonUtil.runWithoutException(this.getLogTag(), 'publishAtStage', () => {
      const stageSubscribers = this.subscribers.get(stage);
      this.debugLog(`stage [${stage}] size=${stageSubscribers?.length}`);
      if (!stageSubscribers) {
        return;
      }
      stageSubscribers?.forEach((callbackFn) => {
        this.debugLog('start to notify subscriber');
        callbackFn?.(data);
      });
    });
    this.debugLog(`end to publish at stage ${stage}`);
  }

  public subscribe(stage: string, callback: ObserverCallback<T>): void {
    if (!this.subscribers.has(stage)) {
      this.subscribers.set(stage, new HashSet());
    }
    const eventSubscribers = this.subscribers.get(stage);
    if (!eventSubscribers?.has(callback)) {
      eventSubscribers?.add(callback);
    }
    this.debugLog(`subscribe end,stage=[${stage}] size=${eventSubscribers?.length}`);
  }

  public unsubscribe(eventType: string, callback: ObserverCallback<T>): void {
    this.subscribers.get(eventType)?.remove(callback);
    this.debugLog(`unsubscribe end,stage=${eventType} size=${this.subscribers.get(eventType)?.length}`);
  }

  public unsubscribeAll(): void {
    this.subscribers = new Map();
    this.infoLog('unsubscribeAll');
  }

  public unsubscribeByType(eventType: string): void {
    this.subscribers.delete(eventType);
    this.infoLog(`unsubscribeByType ${eventType} end`);
  }
}