import { queryClient } from '@/contexts/query-client';

import {
  type DeduplicatedNotification,
  getNotificationStatus,
  type NormalizedNotification,
  normalizeNotification,
} from './normalizers/notification';
import { type NormalizedStatus, normalizeStatus } from './normalizers/status';

import type {
  Account as BaseAccount,
  Group as BaseGroup,
  Poll as BasePoll,
  Relationship as BaseRelationship,
  Status as BaseStatus,
} from 'pl-api';

const importAccount = (account: BaseAccount) =>
  queryClient.setQueryData<BaseAccount>(['accounts', 'entities', account.id], account);

const importGroup = (group: BaseGroup) =>
  queryClient.setQueryData<BaseGroup>(['groups', 'entities', group.id], group);

const importNotification = (notification: DeduplicatedNotification) =>
  queryClient.setQueryData<NormalizedNotification>(
    ['notifications', 'entities', notification.id],
    (existingNotification) =>
      existingNotification?.duplicate ? existingNotification : normalizeNotification(notification),
  );

const importPoll = (poll: BasePoll) =>
  queryClient.setQueryData<BasePoll>(['polls', 'entities', poll.id], poll);

const importRelationship = (relationship: BaseRelationship) =>
  queryClient.setQueryData<BaseRelationship>(
    ['relationships', 'entities', relationship.id],
    relationship,
  );

const importStatus = (status: BaseStatus) =>
  queryClient.setQueryData<NormalizedStatus>(
    ['statuses', 'entities', status.id],
    normalizeStatus(status),
  );

const isEmpty = (object: Record<string, any>) => !Object.values(object).some((value) => value);

type OptionalArray<T> = Array<T | undefined | null>;

const importEntities = (
  entities: {
    accounts?: OptionalArray<BaseAccount>;
    groups?: OptionalArray<BaseGroup>;
    notifications?: OptionalArray<DeduplicatedNotification>;
    polls?: OptionalArray<BasePoll>;
    statuses?: OptionalArray<BaseStatus>;
    relationships?: OptionalArray<BaseRelationship>;
  },
  options = {
    withParents: true,
  },
) => {
  const accounts: Record<string, BaseAccount> = {};
  const groups: Record<string, BaseGroup> = {};
  const notifications: Record<string, DeduplicatedNotification> = {};
  const polls: Record<string, BasePoll> = {};
  const relationships: Record<string, BaseRelationship> = {};
  const statuses: Record<string, BaseStatus> = {};

  const processAccount = (account: BaseAccount, withSelf = true) => {
    if (withSelf) accounts[account.id] = account;

    queryClient.setQueryData<string>(
      ['accounts', 'byAcct', account.acct.toLocaleLowerCase()],
      account.id,
    );

    if (account.moved) processAccount(account.moved);
    if (account.relationship) relationships[account.relationship.id] = account.relationship;
  };

  const processNotification = (notification: DeduplicatedNotification, withSelf = true) => {
    if (withSelf) notifications[notification.id] = notification;

    processAccount(notification.account);
    if (notification.type === 'move') processAccount(notification.target);

    const status = getNotificationStatus(notification);
    if (status) {
      processStatus(status);
    }
  };

  const processStatus = (status: BaseStatus, withSelf = true) => {
    if (withSelf) statuses[status.id] = status;

    if (status.account) {
      processAccount(status.account);
    }

    if (status.quote && 'quoted_status' in status.quote && status.quote.quoted_status)
      processStatus(status.quote.quoted_status);
    if (status.reblog) processStatus(status.reblog);
    if (status.poll) polls[status.poll.id] = status.poll;
    if (status.group) groups[status.group.id] = status.group;
  };

  if (options.withParents) {
    entities.groups?.forEach((group) => group && (groups[group.id] = group));
    entities.polls?.forEach((poll) => poll && (polls[poll.id] = poll));
    entities.relationships?.forEach(
      (relationship) => relationship && (relationships[relationship.id] = relationship),
    );
  }

  entities.accounts?.forEach((account) => account && processAccount(account, options.withParents));
  entities.notifications?.forEach(
    (notification) => notification && processNotification(notification, options.withParents),
  );
  entities.statuses?.forEach((status) => status && processStatus(status, options.withParents));

  if (!isEmpty(accounts)) Object.values(accounts).forEach(importAccount);
  if (!isEmpty(groups)) Object.values(groups).forEach(importGroup);
  if (!isEmpty(notifications)) Object.values(notifications).forEach(importNotification);
  if (!isEmpty(polls)) Object.values(polls).forEach(importPoll);
  if (!isEmpty(relationships)) Object.values(relationships).forEach(importRelationship);
  if (!isEmpty(statuses)) Object.values(statuses).forEach(importStatus);
};

export { importEntities };
