import { HttpError } from "../../shared/api/errors";
import { dictionaryApi, type DictionaryApi } from "./dictionaryApi";
import type { UiDictionaries } from "./dictionaryTypes";

export type DictionaryLoadStatus =
  | "idle"
  | "loading"
  | "success"
  | "error";

export interface DictionaryLoadError {
  message: string;
  requestId: string;
  code: string;
  httpStatus: number;
}

export interface DictionaryState {
  sessionKey: string | null;
  status: DictionaryLoadStatus;
  data: UiDictionaries | null;
  error: DictionaryLoadError | null;
}

type Listener = () => void;

function toLoadError(value: unknown): DictionaryLoadError {
  if (value instanceof HttpError) {
    return {
      message: value.message,
      requestId: value.requestId,
      code: value.code,
      httpStatus: value.httpStatus,
    };
  }
  return {
    message: "系统字典加载失败",
    requestId: "",
    code: "INTERNAL_ERROR",
    httpStatus: 0,
  };
}

export class DictionarySessionStore {
  private state: DictionaryState = {
    sessionKey: null,
    status: "idle",
    data: null,
    error: null,
  };
  private readonly listeners = new Set<Listener>();
  private inFlight: Promise<UiDictionaries> | null = null;
  private generation = 0;

  constructor(private readonly api: DictionaryApi = dictionaryApi) {}

  getSnapshot = (): DictionaryState => this.state;

  subscribe = (listener: Listener): (() => void) => {
    this.listeners.add(listener);
    return () => this.listeners.delete(listener);
  };

  private publish(state: DictionaryState): void {
    this.state = state;
    this.listeners.forEach((listener) => listener());
  }

  load(sessionKey: string, force = false): Promise<UiDictionaries> {
    if (
      !force &&
      this.state.sessionKey === sessionKey &&
      this.state.status === "success" &&
      this.state.data
    ) {
      return Promise.resolve(this.state.data);
    }
    if (!force && this.state.sessionKey === sessionKey && this.inFlight) {
      return this.inFlight;
    }

    const generation = ++this.generation;
    this.publish({
      sessionKey,
      status: "loading",
      data: null,
      error: null,
    });
    const request = this.api
      .getUiDictionaries()
      .then((data) => {
        if (generation === this.generation) {
          this.publish({
            sessionKey,
            status: "success",
            data,
            error: null,
          });
        }
        return data;
      })
      .catch((error: unknown) => {
        if (generation === this.generation) {
          this.publish({
            sessionKey,
            status: "error",
            data: null,
            error: toLoadError(error),
          });
        }
        throw error;
      })
      .finally(() => {
        if (generation === this.generation) this.inFlight = null;
      });
    this.inFlight = request;
    return request;
  }

  retry(sessionKey: string): Promise<UiDictionaries> {
    return this.load(sessionKey, true);
  }

  clear(): void {
    this.generation += 1;
    this.inFlight = null;
    this.publish({
      sessionKey: null,
      status: "idle",
      data: null,
      error: null,
    });
  }
}

export const dictionarySessionStore = new DictionarySessionStore();
