import { View, ViewsDTO } from '../model';
import {
  WixPatternsMonitor,
  FiltersMap,
  DataCapsule,
  WixPatternsContainer,
} from '@wix/bex-core';

export interface ViewsServiceParams {
  readonly viewsStorage: DataCapsule;
  readonly defaultViewStorage: DataCapsule;
  readonly fqdn: string;
  readonly monitor: WixPatternsMonitor;
  readonly errorHandler: WixPatternsContainer['errorHandler'];
  readonly translate: WixPatternsContainer['translate'];
}

export interface AppSettingsViewsSchema<F extends FiltersMap> {
  views: {
    hiddenPresetIds?: Record<string, string | null | undefined>;
    list?: Record<string, View<F> | null | undefined>;
  };
}

export class ViewsService<F extends FiltersMap> {
  readonly viewsStorage;
  readonly defaultViewStorage;
  readonly fqdn;
  readonly monitor;
  readonly errorHandler;
  readonly translate;

  constructor(params: ViewsServiceParams) {
    this.viewsStorage = params.viewsStorage;
    this.defaultViewStorage = params.defaultViewStorage;
    this.fqdn = params.fqdn;
    this.monitor = params.monitor;
    this.errorHandler = params.errorHandler;
    this.translate = params.translate;
  }

  async migrateFromLocalStorage() {
    const { viewsStorage, fqdn } = this;

    try {
      await (
        viewsStorage as { migrateItem?: (key: string) => unknown }
      ).migrateItem?.(fqdn);
    } catch (e) {
      this.monitor.reportError({
        error: e,
      });
    }
  }

  async fetchViews(): Promise<ViewsDTO<F> | null> {
    const { viewsStorage, fqdn, errorHandler } = this;

    const data = await errorHandler.withErrorHandler(
      () =>
        viewsStorage
          .getItem<AppSettingsViewsSchema<F> | null | undefined>(fqdn)
          .catch((e) => {
            if (
              (e as Record<string, unknown> | undefined)?.message ===
              'Key was not found in capsule'
            ) {
              return null;
            }
            throw e;
          }),
      {},
    );

    if (data?.views == null) {
      return null;
    }

    const { list, hiddenPresetIds } = data.views;

    return {
      views:
        list &&
        Object.values(list)
          .filter((view): view is View<F> => view != null)
          .sort(
            (a, b) =>
              new Date(a.createdAt ?? 0).valueOf() -
              new Date(b.createdAt ?? 0).valueOf(),
          ),
      hiddenPresetIds:
        hiddenPresetIds &&
        Object.values(hiddenPresetIds).filter((id): id is string => id != null),
    };
  }

  async fetchDefaultViewId(): Promise<string | null> {
    const { defaultViewStorage, fqdn, errorHandler } = this;

    const data = await errorHandler.withErrorHandler(
      () =>
        defaultViewStorage
          .getItem<{ id: string } | string | null | undefined>(
            `${fqdn}.defaultViewId`,
          )
          .catch((e) => {
            if (
              (e as Record<string, unknown> | undefined)?.message ===
              'Key was not found in capsule'
            ) {
              return null;
            }
            throw e;
          }),
      {},
    );

    return typeof data === 'string' ? data : data?.id ?? null;
  }

  async saveView(view: View<F>, props?: { isRename?: boolean }) {
    const { viewsStorage, fqdn, errorHandler, translate: t } = this;

    await errorHandler.withErrorHandler(
      () =>
        viewsStorage.setItem<AppSettingsViewsSchema<F> | null | undefined>(
          fqdn,
          {
            views: {
              list: {
                [view.id]: {
                  ...view,
                  createdAt: new Date().toISOString(),
                },
              },
            },
          },
        ),
      {},
      {
        statusCodesOverrides: {
          INTERNAL: () => ({
            message: props?.isRename
              ? t('cairo.manageView.Rename.error.toast')
              : t('cairo.manageView.saveView.error.toast'),
          }),
        },
      },
    );
  }

  async deleteView(view: View<F>) {
    const { viewsStorage, fqdn, errorHandler, translate: t } = this;

    await errorHandler.withErrorHandler(
      () =>
        viewsStorage.setItem<AppSettingsViewsSchema<F> | null | undefined>(
          fqdn,
          {
            views: {
              list: {
                [view.id]: null,
              },
            },
          },
        ),
      {},
      {
        statusCodesOverrides: {
          INTERNAL: () => ({
            message: t('cairo.manageView.Delete.CTA.error.toast'),
          }),
        },
      },
    );
  }

  async updateHiddenPreset(idToDelete: string) {
    const { viewsStorage, fqdn, errorHandler, translate: t } = this;
    await errorHandler.withErrorHandler(
      () =>
        viewsStorage.setItem<AppSettingsViewsSchema<F> | null | undefined>(
          fqdn,
          {
            views: {
              hiddenPresetIds: {
                [idToDelete]: idToDelete,
              },
            },
          },
        ),
      {},
      {
        statusCodesOverrides: {
          INTERNAL: () => ({
            message: t('cairo.manageView.Delete.CTA.error.toast'),
          }),
        },
      },
    );
  }

  async updateDefaultView(view?: View<F>) {
    const { defaultViewStorage, fqdn, errorHandler, translate: t } = this;
    await errorHandler.withErrorHandler(
      async () => {
        const defaultViewKey = `${fqdn}.defaultViewId`;
        if (view != null) {
          await defaultViewStorage.setItem<{ id: string } | null | undefined>(
            defaultViewKey,
            {
              id: view.id,
            },
          );
        } else {
          await defaultViewStorage.removeItem(defaultViewKey);
        }
      },
      {},
      {
        statusCodesOverrides: {
          INTERNAL: view
            ? () => ({
                message: t('cairo.manageView.setAsDefault.error.toast', {
                  viewName: view.name,
                }),
              })
            : undefined,
        },
      },
    );
  }
}
