import Noco from '../Noco';
import {
  CacheDelDirection,
  CacheGetType,
  CacheScope,
  MetaTable,
} from '../utils/globals';
import Model from './Model';
import FormView from './FormView';
import GridView from './GridView';
import KanbanView from './KanbanView';
import GalleryView from './GalleryView';
import GridViewColumn from './GridViewColumn';
import Sort from './Sort';
import Filter from './Filter';
import { isSystemColumn, ViewType, ViewTypes } from 'nocodb-sdk';
import GalleryViewColumn from './GalleryViewColumn';
import FormViewColumn from './FormViewColumn';
import Column from './Column';
import NocoCache from '../cache/NocoCache';
import { extractProps } from '../meta/helpers/extractProps';

const { v4: uuidv4 } = require('uuid');
export default class View implements ViewType {
  id?: string;
  title: string;
  uuid?: string;
  password?: string;
  show: boolean;
  is_default: boolean;
  order: number;
  type: ViewTypes;
  lock_type?: ViewType['lock_type'];

  fk_model_id: string;
  model?: Model;
  view?: FormView | GridView | KanbanView | GalleryView;
  columns?: Array<FormViewColumn | GridViewColumn | GalleryViewColumn>;

  sorts: Sort[];
  filter: Filter;
  project_id?: string;
  base_id?: string;
  show_system_fields?: boolean;
  meta?: any;

  constructor(data: View) {
    Object.assign(this, data);
  }

  async getModel(ncMeta = Noco.ncMeta): Promise<Model> {
    return (this.model = await Model.getByIdOrName(
      { id: this.fk_model_id },
      ncMeta
    ));
  }

  async getModelWithInfo(ncMeta = Noco.ncMeta): Promise<Model> {
    return (this.model = await Model.getWithInfo(
      { id: this.fk_model_id },
      ncMeta
    ));
  }

  async getView<T>(): Promise<T> {
    switch (this.type) {
      case ViewTypes.GRID:
        this.view = await GridView.get(this.id);
        break;
      case ViewTypes.KANBAN:
        this.view = await KanbanView.get(this.id);
        break;
      case ViewTypes.GALLERY:
        this.view = await GalleryView.get(this.id);
        break;
      case ViewTypes.FORM:
        this.view = await FormView.get(this.id);
        break;
    }
    return <T>this.view;
  }

  async getViewWithInfo(
    ncMeta = Noco.ncMeta
  ): Promise<FormView | GridView | KanbanView | GalleryView> {
    switch (this.type) {
      case ViewTypes.GRID:
        this.view = await GridView.getWithInfo(this.id, ncMeta);
        break;
      case ViewTypes.KANBAN:
        this.view = await KanbanView.get(this.id, ncMeta);
        break;
      case ViewTypes.GALLERY:
        this.view = await GalleryView.get(this.id, ncMeta);
        break;
      case ViewTypes.FORM:
        this.view = await FormView.get(this.id, ncMeta);
        break;
    }
    return this.view;
  }

  public static async get(viewId: string, ncMeta = Noco.ncMeta) {
    let view =
      viewId &&
      (await NocoCache.get(
        `${CacheScope.VIEW}:${viewId}`,
        CacheGetType.TYPE_OBJECT
      ));
    if (!view) {
      view = await ncMeta.metaGet2(null, null, MetaTable.VIEWS, viewId);
      await NocoCache.set(`${CacheScope.VIEW}:${view.id}`, view);
    }

    return view && new View(view);
  }

  public static async getByTitleOrId(
    { fk_model_id, titleOrId }: { titleOrId: string; fk_model_id: string },
    ncMeta = Noco.ncMeta
  ) {
    const viewId =
      titleOrId &&
      (await NocoCache.get(
        `${CacheScope.VIEW}:${fk_model_id}:${titleOrId}`,
        CacheGetType.TYPE_OBJECT
      ));
    if (!viewId) {
      const view = await ncMeta.metaGet2(
        null,
        null,
        MetaTable.VIEWS,
        { fk_model_id },
        null,
        {
          _or: [
            {
              id: {
                eq: titleOrId,
              },
            },
            {
              title: {
                eq: titleOrId,
              },
            },
          ],
        }
      );
      // todo: cache - titleOrId can be viewId so we need a different scope here
      await NocoCache.set(
        `${CacheScope.VIEW}:${fk_model_id}:${titleOrId}`,
        view.id
      );
      await NocoCache.set(`${CacheScope.VIEW}:${fk_model_id}:${view.id}`, view);
      return view && new View(view);
    }
    return viewId && this.get(viewId?.id || viewId);
  }

  public static async getDefaultView(
    fk_model_id: string,
    ncMeta = Noco.ncMeta
  ) {
    let view =
      fk_model_id &&
      (await NocoCache.get(
        `${CacheScope.VIEW}:${fk_model_id}:default`,
        CacheGetType.TYPE_OBJECT
      ));
    if (!view) {
      view = await ncMeta.metaGet2(
        null,
        null,
        MetaTable.VIEWS,
        {
          fk_model_id,
          is_default: 1,
        },
        null
      );
      await NocoCache.set(`${CacheScope.VIEW}:${fk_model_id}:default`, view);
    }
    return view && new View(view);
  }

  public static async list(modelId: string, ncMeta = Noco.ncMeta) {
    let viewsList = await NocoCache.getList(CacheScope.VIEW, [modelId]);
    if (!viewsList.length) {
      viewsList = await ncMeta.metaList2(null, null, MetaTable.VIEWS, {
        condition: {
          fk_model_id: modelId,
        },
        orderBy: {
          order: 'asc',
        },
      });
      await NocoCache.setList(CacheScope.VIEW, [modelId], viewsList);
    }
    viewsList.sort(
      (a, b) =>
        (a.order != null ? a.order : Infinity) -
        (b.order != null ? b.order : Infinity)
    );
    return viewsList?.map((v) => new View(v));
  }

  public async getFilters(ncMeta = Noco.ncMeta) {
    return (this.filter = (await Filter.getFilterObject(
      {
        viewId: this.id,
      },
      ncMeta
    )) as any);
  }

  public async getSorts(ncMeta = Noco.ncMeta) {
    return (this.sorts = await Sort.list({ viewId: this.id }, ncMeta));
  }

  static async insert(
    view: Partial<View> &
      Partial<FormView | GridView | GalleryView | KanbanView> & {
        copy_from_id?: string;
        created_at?;
        updated_at?;
      },
    ncMeta = Noco.ncMeta
  ) {
    // get order value
    const order = await ncMeta.metaGetNextOrder(MetaTable.VIEWS, {
      fk_model_id: view.fk_model_id,
    });

    const insertObj = {
      id: view.id,
      title: view.title,
      show: true,
      is_default: view.is_default,
      order,
      type: view.type,
      fk_model_id: view.fk_model_id,
      project_id: view.project_id,
      base_id: view.base_id,
      created_at: view.created_at,
      updated_at: view.updated_at,
    };

    // get project and base id if missing
    if (!(view.project_id && view.base_id)) {
      const model = await Model.getByIdOrName({ id: view.fk_model_id }, ncMeta);
      insertObj.project_id = model.project_id;
      insertObj.base_id = model.base_id;
    }

    const copyFromView =
      view.copy_from_id && (await View.get(view.copy_from_id, ncMeta));
    await copyFromView?.getView();

    const { id: view_id } = await ncMeta.metaInsert2(
      null,
      null,
      MetaTable.VIEWS,
      insertObj
    );

    await NocoCache.appendToList(
      CacheScope.VIEW,
      [view.fk_model_id],
      `${CacheScope.VIEW}:${view_id}`
    );

    // insert view metadata based on view type
    switch (view.type) {
      case ViewTypes.GRID:
        await GridView.insert(
          {
            ...(view as GridView),
            fk_view_id: view_id,
          },
          ncMeta
        );
        break;
      case ViewTypes.GALLERY:
        await GalleryView.insert(
          {
            ...(copyFromView?.view || {}),
            ...view,
            fk_view_id: view_id,
          },
          ncMeta
        );
        break;
      case ViewTypes.FORM:
        await FormView.insert(
          {
            heading: view.title,
            ...(copyFromView?.view || {}),
            ...view,
            fk_view_id: view_id,
          },
          ncMeta
        );
        break;
    }

    let columns: any[] = await (
      await Model.getByIdOrName({ id: view.fk_model_id }, ncMeta)
    ).getColumns(ncMeta);

    if (copyFromView) {
      const sorts = await copyFromView.getSorts(ncMeta);
      const filters = await copyFromView.getFilters(ncMeta);
      columns = await copyFromView.getColumns(ncMeta);

      for (const sort of sorts) {
        await Sort.insert(
          {
            ...sort,
            fk_view_id: view_id,
            id: null,
          },
          ncMeta
        );
      }

      for (const filter of filters.children) {
        await Filter.insert(
          {
            ...filter,
            fk_view_id: view_id,
            id: null,
          },
          ncMeta
        );
      }
    }
    {
      let order = 1;
      let galleryShowLimit = 0;
      for (const vCol of columns) {
        let show = 'show' in vCol ? vCol.show : true;

        if (view.type === ViewTypes.GALLERY) {
          const galleryView = await GalleryView.get(view_id, ncMeta);
          if (vCol.id === galleryView.fk_cover_image_col_id || vCol.pv || galleryShowLimit < 3) {
            show = true;
            galleryShowLimit++;
          } else {
            show = false;
          }
        }

        // if columns is list of virtual columns then get the parent column
        const col = vCol.fk_column_id
          ? await Column.get({ colId: vCol.fk_column_id }, ncMeta)
          : vCol;

        if (isSystemColumn(col)) show = false;
        await View.insertColumn(
          {
            order: order++,
            ...col,
            ...vCol,
            view_id,
            fk_column_id: vCol.fk_column_id || vCol.id,
            show,
            id: null,
          },
          ncMeta
        );
      }
    }

    return View.get(view_id, ncMeta);
  }

  static async insertColumnToAllViews(
    param: {
      fk_column_id: any;
      fk_model_id: any;
      order;
      show;
    },
    ncMeta = Noco.ncMeta
  ) {
    const insertObj = {
      fk_column_id: param.fk_column_id,
      fk_model_id: param.fk_model_id,
      order: param.order,
      show: param.show,
    };
    const views = await this.list(param.fk_model_id, ncMeta);

    for (const view of views) {
      switch (view.type) {
        case ViewTypes.GRID:
          await GridViewColumn.insert(
            {
              ...insertObj,
              fk_view_id: view.id,
            },
            ncMeta
          );
          break;
        case ViewTypes.GALLERY:
          await GalleryViewColumn.insert(
            {
              ...insertObj,
              fk_view_id: view.id,
            },
            ncMeta
          );
          break;
      }
    }
  }

  static async insertColumn(
    param: {
      view_id: any;
      order;
      show;
      fk_column_id;
      id?: string;
    } & Partial<FormViewColumn>,
    ncMeta = Noco.ncMeta
  ) {
    const view = await this.get(param.view_id, ncMeta);

    let col;
    switch (view.type) {
      case ViewTypes.GRID:
        {
          col = await GridViewColumn.insert(
            {
              ...param,
              fk_view_id: view.id,
            },
            ncMeta
          );
        }
        break;
      case ViewTypes.GALLERY:
        {
          col = await GalleryViewColumn.insert(
            {
              ...param,
              fk_view_id: view.id,
            },
            ncMeta
          );
        }
        break;
      case ViewTypes.FORM:
        {
          col = await FormViewColumn.insert(
            {
              ...param,
              fk_view_id: view.id,
            },
            ncMeta
          );
        }
        break;
    }

    return col;
  }

  static async listWithInfo(id: string, ncMeta = Noco.ncMeta) {
    const list = await this.list(id, ncMeta);
    for (const item of list) {
      await item.getViewWithInfo(ncMeta);
    }
    return list;
  }

  static async getColumns(
    viewId: string,
    ncMeta = Noco.ncMeta
  ): Promise<Array<GridViewColumn | FormViewColumn | GalleryViewColumn>> {
    let columns: Array<GridViewColumn | any> = [];
    const view = await this.get(viewId, ncMeta);

    // todo:  just get - order & show props
    switch (view.type) {
      case ViewTypes.GRID:
        columns = await GridViewColumn.list(viewId, ncMeta);
        break;

      case ViewTypes.GALLERY:
        columns = await GalleryViewColumn.list(viewId, ncMeta);
        break;
      case ViewTypes.FORM:
        columns = await FormViewColumn.list(viewId, ncMeta);
        break;
    }

    return columns;
  }

  async getColumns(ncMeta = Noco.ncMeta) {
    return (this.columns = await View.getColumns(this.id, ncMeta));
  }

  static async updateColumn(
    viewId: string,
    colId: string,
    colData: {
      order?: number;
      show?: boolean;
    },
    ncMeta = Noco.ncMeta
  ): Promise<Array<GridViewColumn | any>> {
    const columns: Array<GridViewColumn | any> = [];
    const view = await this.get(viewId, ncMeta);
    let table;
    let cacheScope;
    switch (view.type) {
      case ViewTypes.GRID:
        table = MetaTable.GRID_VIEW_COLUMNS;
        cacheScope = CacheScope.GRID_VIEW_COLUMN;
        break;
      case ViewTypes.GALLERY:
        table = MetaTable.GALLERY_VIEW_COLUMNS;
        cacheScope = CacheScope.GALLERY_VIEW_COLUMN;
        break;
      case ViewTypes.KANBAN:
        table = MetaTable.KANBAN_VIEW_COLUMNS;
        cacheScope = CacheScope.KANBAN_VIEW_COLUMN;
        break;
      case ViewTypes.FORM:
        table = MetaTable.FORM_VIEW_COLUMNS;
        cacheScope = CacheScope.FORM_VIEW_COLUMN;
        break;
    }
    const updateObj = extractProps(colData, ['order', 'show']);
    // get existing cache
    const key = `${cacheScope}:${colId}`;
    let o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
    if (o) {
      // update data
      o = { ...o, ...updateObj };
      // set cache
      await NocoCache.set(key, o);
    }
    // set meta
    await ncMeta.metaUpdate(null, null, table, updateObj, colId);

    return columns;
  }

  static async insertOrUpdateColumn(
    viewId: string,
    fkColId: string,
    colData: {
      order: number;
      show: boolean;
    },
    ncMeta = Noco.ncMeta
  ): Promise<GridViewColumn | FormViewColumn | GalleryViewColumn | any> {
    const view = await this.get(viewId);
    const table = this.extractViewColumnsTableName(view);
    console.log(table);

    const existingCol = await ncMeta.metaGet2(null, null, table, {
      fk_view_id: viewId,
      fk_column_id: fkColId,
    });

    if (existingCol) {
      await ncMeta.metaUpdate(
        null,
        null,
        table,
        {
          order: colData.order,
          show: colData.show,
        },
        existingCol.id
      );
      return { ...existingCol, ...colData };
    } else {
      switch (view.type) {
        case ViewTypes.GRID:
          return await GridViewColumn.insert({
            fk_view_id: viewId,
            fk_column_id: fkColId,
            order: colData.order,
            show: colData.show,
          });
        case ViewTypes.GALLERY:
          return await GalleryViewColumn.insert({
            fk_view_id: viewId,
            fk_column_id: fkColId,
            order: colData.order,
            show: colData.show,
          });
        case ViewTypes.KANBAN:
          // TODO: Use the following when KanbanViewColumn is ready to avoid cache issue
          // return await KanbanViewColumn.insert({
          //   fk_view_id: viewId,
          //   fk_column_id: fkColId,
          //   order: colData.order,
          //   show: colData.show,
          // });
          break;
        case ViewTypes.FORM:
          return await FormViewColumn.insert({
            fk_view_id: viewId,
            fk_column_id: fkColId,
            order: colData.order,
            show: colData.show,
          });
      }
      return await ncMeta.metaInsert2(view.project_id, view.base_id, table, {
        fk_view_id: viewId,
        fk_column_id: fkColId,
        order: colData.order,
        show: colData.show,
      });
    }
  }

  static async getByUUID(uuid: string, ncMeta = Noco.ncMeta) {
    const view = await ncMeta.metaGet2(null, null, MetaTable.VIEWS, {
      uuid,
    });

    return view && new View(view);
  }

  static async share(viewId, ncMeta = Noco.ncMeta) {
    const view = await this.get(viewId);
    if (!view.uuid) {
      const uuid = uuidv4();
      view.uuid = uuid;
      // get existing cache
      const key = `${CacheScope.VIEW}:${view.id}`;
      const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
      if (o) {
        // update data
        o.uuid = uuid;
        // set cache
        await NocoCache.set(key, o);
      }
      // set meta
      await ncMeta.metaUpdate(
        null,
        null,
        MetaTable.VIEWS,
        {
          uuid: view.uuid,
        },
        viewId
      );
    }
    if (!view.meta) {
      const defaultMeta = {
        allowCSVDownload: true,
      };
      // get existing cache
      const key = `${CacheScope.VIEW}:${view.id}`;
      const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
      if (o) {
        // update data
        o.meta = JSON.stringify(defaultMeta);
        // set cache
        await NocoCache.set(key, o);
      }
      // set meta
      await ncMeta.metaUpdate(
        null,
        null,
        MetaTable.VIEWS,
        {
          meta: JSON.stringify(defaultMeta),
        },
        viewId
      );
      view.meta = defaultMeta;
    }
    return view;
  }

  static async passwordUpdate(
    viewId: string,
    { password }: { password: string },
    ncMeta = Noco.ncMeta
  ) {
    // get existing cache
    const key = `${CacheScope.VIEW}:${viewId}`;
    const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
    if (o) {
      // update data
      o.password = password;
      // set cache
      await NocoCache.set(key, o);
    }
    // set meta
    await ncMeta.metaUpdate(
      null,
      null,
      MetaTable.VIEWS,
      {
        password,
      },
      viewId
    );
  }

  static async sharedViewDelete(viewId, ncMeta = Noco.ncMeta) {
    // get existing cache
    const key = `${CacheScope.VIEW}:${viewId}`;
    const o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
    if (o) {
      // update data
      o.uuid = null;
      // set cache
      await NocoCache.set(key, o);
    }
    // set meta
    await ncMeta.metaUpdate(
      null,
      null,
      MetaTable.VIEWS,
      {
        uuid: null,
      },
      viewId
    );
  }

  static async update(
    viewId: string,
    body: {
      title?: string;
      order?: number;
      show_system_fields?: boolean;
      lock_type?: string;
      password?: string;
      uuid?: string;
      meta?: any;
    },
    ncMeta = Noco.ncMeta
  ) {
    const updateObj = extractProps(body, [
      'title',
      'order',
      'show_system_fields',
      'lock_type',
      'password',
      'meta',
      'uuid',
    ]);
    updateObj.meta = JSON.stringify(updateObj.meta);
    // get existing cache
    const key = `${CacheScope.VIEW}:${viewId}`;
    let o = await NocoCache.get(key, CacheGetType.TYPE_OBJECT);
    if (o) {
      // update data
      o = {
        ...o,
        ...updateObj,
      };
      if (o.is_default) {
        await NocoCache.set(`${CacheScope.VIEW}:${o.fk_model_id}:default`, o);
      }
      // set cache
      await NocoCache.set(key, o);
    }
    // set meta
    await ncMeta.metaUpdate(null, null, MetaTable.VIEWS, updateObj, viewId);
    return this.get(viewId);
  }

  // @ts-ignore
  static async delete(viewId, ncMeta = Noco.ncMeta) {
    const view = await this.get(viewId);
    await Sort.deleteAll(viewId);
    await Filter.deleteAll(viewId);
    const table = this.extractViewTableName(view);
    const tableScope = this.extractViewTableNameScope(view);
    const columnTable = this.extractViewColumnsTableName(view);
    const columnTableScope = this.extractViewColumnsTableNameScope(view);
    await ncMeta.metaDelete(null, null, columnTable, {
      fk_view_id: viewId,
    });
    await NocoCache.deepDel(
      tableScope,
      `${tableScope}:${viewId}`,
      CacheDelDirection.CHILD_TO_PARENT
    );
    await ncMeta.metaDelete(null, null, table, {
      fk_view_id: viewId,
    });
    await NocoCache.deepDel(
      columnTableScope,
      `${columnTableScope}:${viewId}`,
      CacheDelDirection.CHILD_TO_PARENT
    );
    await ncMeta.metaDelete(null, null, MetaTable.VIEWS, viewId);
    await NocoCache.deepDel(
      CacheScope.VIEW,
      `${CacheScope.VIEW}:${viewId}`,
      CacheDelDirection.CHILD_TO_PARENT
    );
    await NocoCache.del(`${CacheScope.VIEW}:${view.fk_model_id}:${view.title}`);
    await NocoCache.del(`${CacheScope.VIEW}:${view.fk_model_id}:${view.id}`);
  }

  private static extractViewColumnsTableName(view: View) {
    let table;
    switch (view.type) {
      case ViewTypes.GRID:
        table = MetaTable.GRID_VIEW_COLUMNS;
        break;
      case ViewTypes.GALLERY:
        table = MetaTable.GALLERY_VIEW_COLUMNS;
        break;
      case ViewTypes.KANBAN:
        table = MetaTable.KANBAN_VIEW_COLUMNS;
        break;
      case ViewTypes.FORM:
        table = MetaTable.FORM_VIEW_COLUMNS;
        break;
    }
    return table;
  }

  private static extractViewTableName(view: View) {
    let table;
    switch (view.type) {
      case ViewTypes.GRID:
        table = MetaTable.GRID_VIEW;
        break;
      case ViewTypes.GALLERY:
        table = MetaTable.GALLERY_VIEW;
        break;
      case ViewTypes.KANBAN:
        table = MetaTable.KANBAN_VIEW;
        break;
      case ViewTypes.FORM:
        table = MetaTable.FORM_VIEW;
        break;
    }
    return table;
  }

  private static extractViewColumnsTableNameScope(view: View) {
    let scope;
    switch (view.type) {
      case ViewTypes.GRID:
        scope = CacheScope.GRID_VIEW_COLUMN;
        break;
      case ViewTypes.GALLERY:
        scope = CacheScope.GALLERY_VIEW_COLUMN;
        break;
      case ViewTypes.KANBAN:
        scope = CacheScope.KANBAN_VIEW_COLUMN;
        break;
      case ViewTypes.FORM:
        scope = CacheScope.FORM_VIEW_COLUMN;
        break;
    }
    return scope;
  }

  private static extractViewTableNameScope(view: View) {
    let scope;
    switch (view.type) {
      case ViewTypes.GRID:
        scope = CacheScope.GRID_VIEW;
        break;
      case ViewTypes.GALLERY:
        scope = CacheScope.GALLERY_VIEW;
        break;
      case ViewTypes.KANBAN:
        scope = CacheScope.KANBAN_VIEW;
        break;
      case ViewTypes.FORM:
        scope = CacheScope.FORM_VIEW;
        break;
    }
    return scope;
  }

  static async showAllColumns(
    viewId,
    ignoreColdIds = [],
    ncMeta = Noco.ncMeta
  ) {
    const view = await this.get(viewId);
    const table = this.extractViewColumnsTableName(view);
    const scope = this.extractViewColumnsTableNameScope(view);

    const columns = await view
      .getModel(ncMeta)
      .then((meta) => meta.getColumns());
    const viewColumns = await this.getColumns(viewId, ncMeta);
    const availableColumnsInView = viewColumns.map(
      (column) => column.fk_column_id
    );

    // get existing cache
    const dataList = await NocoCache.getList(scope, [viewId]);
    if (dataList?.length) {
      for (const o of dataList) {
        if (!ignoreColdIds?.length || !ignoreColdIds.includes(o.fk_column_id)) {
          // set data
          o.show = true;
          // set cache
          await NocoCache.set(`${scope}:${o.id}`, o);
        }
      }
    }

    // insert or update view column
    for (const col of columns) {
      if (ignoreColdIds?.includes(col.id)) continue;

      const colIndex = availableColumnsInView.indexOf(col.id);
      if (colIndex > -1) {
        await this.updateColumn(
          viewId,
          viewColumns[colIndex].id,
          { show: true },
          ncMeta
        );
      } else {
        await this.insertColumn(
          {
            view_id: viewId,
            order: await ncMeta.metaGetNextOrder(table, {
              fk_view_id: viewId,
            }),
            show: true,
            fk_column_id: col.id,
          },
          ncMeta
        );
      }

      // return await ncMeta.metaUpdate(
      //   null,
      //   null,
      //   table,
      //   { show: true },
      //   {
      //     fk_view_id: viewId,
      //   },
      //   ignoreColdIds?.length
      //     ? {
      //         _not: {
      //           fk_column_id: {
      //             in: ignoreColdIds,
      //           },
      //         },
      //       }
      //     : null
      // );
    }
  }

  static async hideAllColumns(
    viewId,
    ignoreColdIds = [],
    ncMeta = Noco.ncMeta
  ) {
    const view = await this.get(viewId);
    const table = this.extractViewColumnsTableName(view);
    const scope = this.extractViewColumnsTableNameScope(view);
    // get existing cache
    const dataList = await NocoCache.getList(scope, [viewId]);
    if (dataList?.length) {
      for (const o of dataList) {
        if (!ignoreColdIds?.length || !ignoreColdIds.includes(o.fk_column_id)) {
          // set data
          o.show = false;
          // set cache
          await NocoCache.set(`${scope}:${o.id}`, o);
        }
      }
    }
    // set meta
    return await ncMeta.metaUpdate(
      null,
      null,
      table,
      { show: false },
      {
        fk_view_id: viewId,
      },
      ignoreColdIds?.length
        ? {
            _not: {
              fk_column_id: {
                in: ignoreColdIds,
              },
            },
          }
        : null
    );
  }

  async delete() {
    await View.delete(this.id);
  }

  static async shareViewList(tableId, ncMeta = Noco.ncMeta) {
    let sharedViews = await NocoCache.getList(CacheScope.VIEW, [tableId]);
    if (!sharedViews.length) {
      sharedViews = await ncMeta.metaList2(null, null, MetaTable.VIEWS, {
        xcCondition: {
          fk_model_id: {
            eq: tableId,
          },
          _not: {
            uuid: {
              eq: null,
            },
          },
        },
      });
      await NocoCache.setList(CacheScope.VIEW, [tableId], sharedViews);
    }
    sharedViews = sharedViews.filter((v) => v.uuid !== null);
    return sharedViews?.map((v) => new View(v));
  }
}
