import { computed, reactive } from "vue";

export class StateRoot {
  protected computed<F extends (state: Omit<this, "reactive">) => any>(fn: F) {
    return fn as ReturnType<F>;
  }
  reactive = () => {
    return createReactive(this) as Omit<this, "reactive">;
  };
}
function createReactive(obj: any) {
  const data: any = {};
  for (const name in obj) {
    if (name === "reactive" || name === "computed" || name === "proxy")
      continue;
    const objItem: any = obj[name];
    if (objItem instanceof Function) {
      data[name] = computed(() => objItem(state));
    } else {
      data[name] = objItem;
    }
  }
  const state: any = reactive(data);
  return state;
}

class ListItemBase {
  _id?: string = "";
}

class StatePageList<T extends ListItemBase, Q> extends StateRoot {
  size = 20;
  page = 1;
  total = 0;
  list: T[] = [];
  query: Q = {} as any;
  fields = ""; //字段使用逗号隔开
  loading = false;

  canLoadNext = this.computed((state) => {
    const { size, page, total } = state;
    return page * size < total;
  });
}

/**
 * 分页列表控制器
 */
export class ListController<T extends ListItemBase, Q> {
  state = new StatePageList<T, Q>().reactive();
  request: any;
  dataList: any[] = [];
  httpGet?: (
    page: number,
    size: number,
    query?: any,
    fields?: string
  ) => Promise<any>;
  httpDelete?: (id: string) => Promise<any>;
  httpDetail?: (id: string) => Promise<any>;
  httpAdd?: (query?: any) => Promise<any>;
  httpSave?: (item: T) => Promise<any>;
  hasLimit = false;
  listFilter: ((item: T) => T) | null = null;

  constructor(
    request: any,
    get?: () => (
      page: number,
      size: number,
      query?: any,
      fields?: string
    ) => Promise<any>
  ) {
    this.request = request;

    if (get) {
      this.httpGet = get();
    }
  }

  crud(
    prefix: string,
    type: "list" | "create" | "update" | "delete" | "detail",
    data?: any
  ) {
    const config: any = { method: "POST" };
    if (type == "list") {
      config.method = "GET";
      config.params = data;
    } else if (type == "detail") {
      config.method = "GET";
    } else {
      config.data = data;
    }
    let uri = `${prefix}/${type}`;
    if (type == "delete" || type == "detail") uri += `/${data}`;

    return this.request(uri, config);
  }
  setCrudPrefix(prefix: string) {
    this.httpGet = (
      page: number,
      size: number,
      query: any,
      fields?: string
    ) => {
      return this.crud(prefix, "list", {
        page,
        size,
        query,
        fields,
      });
    };
    this.httpAdd = (data: any) => {
      return this.crud(prefix, "create", data);
    };
    this.httpSave = (data: any) => {
      return this.crud(prefix, "update", data);
    };
    this.httpDelete = (id: string) => {
      return this.crud(prefix, "delete", id);
    };
    this.httpDetail = (id: string) => {
      return this.crud(prefix, "detail", id);
    };
  }
  async resumeShow() {
    if (this.state.list.length < 1) {
      await this.loadPage(1);
    }
  }
  //刷新页面，重新获取数据
  async fresh() {
    await this.loadPage(this.state.page, this.state.size);
    if (this.state.list.length == 0 && this.state.page != 1) {
      await this.loadPage(this.state.page - 1, this.state.size);
    }
  }

  loadNextPage = () => {
    return this.loadPage(this.state.page + 1);
  };

  async loadPage(page: number, size?: number): Promise<boolean> {
    if (!this.httpGet) return false;
    this.state.loading = true;

    if (!size) size = this.state.size;
    const ret = await this.httpGet(
      page,
      size,
      this.state.query,
      this.state.fields
    );
    this.state.loading = false;

    if (ret.errorNo == 200) {
      const result = ret.result;
      this.state.page = page;
      this.state.size = size;

      let list = [];
      if (this.listFilter) {
        list = result.list.map(this.listFilter) || [];
      } else {
        list = [...result.list];
      }
      if (page == 1 || !this.hasLimit) {
        this.dataList = [...result.list];
        this.state.list = list;
      } else {
        this.state.list = [...this.state.list, ...list];
        this.dataList = [...this.dataList, ...result.list];
      }
      setTimeout(() => {
        this.state.total = result.total;
      }, 100);
      return true;
    }

    return false;
  }
  async addItem(data: ListItemBase) {
    if (!this.httpAdd) return { errorNo: 500, errorDesc: "httpAdd 未定义" };
    this.state.loading = true;
    const ret = await this.httpAdd(data);
    this.state.loading = false;
    if (ret.errorNo == 200) {
      await this.fresh();
    }
    return ret;
  }

  async deleteItem(id: string) {
    if (!this.httpDelete) return;

    this.state.loading = true;

    const ret = await this.httpDelete(id);
    this.state.loading = false;

    if (ret.errorNo == 200) {
      await this.fresh();
    }
    return ret;
  }

  async saveItem(item: T) {
    if (!this.httpSave) return;
    this.state.loading = true;
    const ret = await this.httpSave(item);
    this.state.loading = false;
    if (ret.errorNo == 200) {
      await this.fresh();
    }
    return ret;
  }
  async itemDetail(id: string) {
    if (!this.httpDetail) return;
    this.state.loading = true;
    const ret = await this.httpDetail(id);
    this.state.loading = false;
    return ret;
  }
}
