/*!
 * Copyright (c) 2024 PLANKA Software GmbH
 * Licensed under the Fair Use License: https://github.com/plankanban/planka/blob/master/LICENSE.md
 */

import { attr, many, oneToOne } from 'redux-orm';

import BaseModel from './BaseModel';
import ActionTypes from '../constants/ActionTypes';
import { UserRoles } from '../constants/Enums';

export default class extends BaseModel {
  static modelName = 'Project';

  static fields = {
    id: attr(),
    name: attr(),
    description: attr(),
    backgroundType: attr(),
    backgroundGradient: attr(),
    isHidden: attr(),
    isFavorite: attr({
      getDefault: () => false,
    }),
    ownerProjectManagerId: oneToOne({
      to: 'ProjectManager',
      as: 'ownerProjectManager',
      relatedName: 'ownedProject',
    }),
    backgroundImageId: oneToOne({
      to: 'BackgroundImage',
      as: 'backgroundImage',
      relatedName: 'backgroundedProject', // TODO: rename?
    }),
    managerUsers: many({
      to: 'User',
      through: 'ProjectManager',
      relatedName: 'managerProjects',
    }),
  };

  static reducer({ type, payload }, Project) {
    switch (type) {
      case ActionTypes.LOCATION_CHANGE_HANDLE:
        if (payload.projects) {
          payload.projects.forEach((project) => {
            Project.upsert(project);
          });
        }

        break;
      case ActionTypes.SOCKET_RECONNECT_HANDLE:
        Project.all().delete();

        payload.projects.forEach((project) => {
          Project.upsert(project);
        });

        break;
      case ActionTypes.CORE_INITIALIZE:
      case ActionTypes.BOARD_FETCH__SUCCESS:
        payload.projects.forEach((project) => {
          Project.upsert(project);
        });

        break;
      case ActionTypes.USER_UPDATE_HANDLE:
        Project.all()
          .toModelArray()
          .forEach((projectModel) => {
            if (!payload.projectIds.includes(projectModel.id)) {
              projectModel.deleteWithRelated(true);
            }
          });

        if (payload.projects) {
          payload.projects.forEach((project) => {
            Project.upsert(project);
          });
        }

        break;
      case ActionTypes.PROJECT_CREATE__SUCCESS:
      case ActionTypes.PROJECT_CREATE_HANDLE:
      case ActionTypes.PROJECT_UPDATE__SUCCESS:
        Project.upsert(payload.project);

        break;
      case ActionTypes.PROJECT_UPDATE:
        Project.withId(payload.id).update(payload.data);

        break;
      case ActionTypes.PROJECT_UPDATE_HANDLE: {
        const projectModel = Project.withId(payload.project.id);

        if (projectModel) {
          if (payload.isAvailable) {
            projectModel.boards.toModelArray().forEach((boardModel) => {
              if (!payload.boardIds.includes(boardModel.id)) {
                boardModel.deleteWithRelated(true);
              }
            });
          } else {
            projectModel.deleteWithRelated(true);
          }
        }

        Project.upsert(payload.project);

        break;
      }
      case ActionTypes.PROJECT_DELETE:
        Project.withId(payload.id).deleteWithRelated();

        break;
      case ActionTypes.PROJECT_DELETE__SUCCESS:
      case ActionTypes.PROJECT_DELETE_HANDLE: {
        const projectModel = Project.withId(payload.project.id);

        if (projectModel) {
          projectModel.deleteWithRelated();
        }

        break;
      }
      case ActionTypes.PROJECT_MANAGER_CREATE_HANDLE: {
        const projectModel = Project.withId(payload.projectManager.projectId);

        if (projectModel) {
          if (payload.isProjectAvailable) {
            projectModel.boards.toModelArray().forEach((boardModel) => {
              if (payload.boardIds.includes(boardModel.id)) {
                if (payload.isCurrentUser) {
                  boardModel.notificationServices.delete();
                }
              } else {
                boardModel.deleteWithRelated(true);
              }
            });
          } else {
            projectModel.deleteWithRelated(true);
          }
        }

        if (payload.project) {
          Project.upsert(payload.project);
        }

        break;
      }
      case ActionTypes.BOARD_MEMBERSHIP_CREATE_HANDLE:
        if (!payload.isProjectAvailable) {
          const projectModel = Project.withId(payload.boardMembership.projectId);

          if (projectModel) {
            projectModel.deleteWithRelated(true);
          }
        }

        if (payload.project) {
          Project.upsert(payload.project);
        }

        break;
      default:
    }
  }

  static getSharedQuerySet() {
    return this.filter({
      ownerProjectManagerId: null,
    }).orderBy(['id.length', 'id']);
  }

  getManagersQuerySet() {
    return this.managers.orderBy(['id.length', 'id']);
  }

  getBackgroundImagesQuerySet() {
    return this.backgroundImages.orderBy(['id.length', 'id']);
  }

  getBaseCustomFieldGroupsQuerySet() {
    return this.baseCustomFieldGroups.orderBy(['id.length', 'id']);
  }

  getBoardsQuerySet() {
    return this.boards.orderBy(['position', 'id.length', 'id']);
  }

  getBoardsModelArrayForUserWithId(userId) {
    return this.getBoardsQuerySet()
      .toModelArray()
      .filter((boardModel) => boardModel.hasMembershipWithUserId(userId));
  }

  getBoardsModelArrayAvailableForUser(userModel) {
    if (this.isExternalAccessibleForUser(userModel)) {
      return this.getBoardsQuerySet().toModelArray();
    }

    return this.getBoardsModelArrayForUserWithId(userModel.id);
  }

  hasManagerWithUserId(userId) {
    return this.managers
      .filter({
        userId,
      })
      .exists();
  }

  hasMembershipWithUserIdInAnyBoard(userId) {
    return this.boards
      .toModelArray()
      .some((boardModel) => boardModel.hasMembershipWithUserId(userId));
  }

  isExternalAccessibleForUser(userModel) {
    if (!this.ownerProjectManagerId && userModel.role === UserRoles.ADMIN) {
      return true;
    }

    return this.hasManagerWithUserId(userModel.id);
  }

  isAvailableForUser(userModel) {
    return (
      this.isExternalAccessibleForUser(userModel) ||
      this.hasMembershipWithUserIdInAnyBoard(userModel.id)
    );
  }

  deleteRelated(soft) {
    this.managers.delete();

    this.backgroundImages.toModelArray().forEach((backgroundImageModel) => {
      backgroundImageModel.deleteWithRelated();
    });

    this.baseCustomFieldGroups.toModelArray().forEach((baseCustomFieldGroupModel) => {
      baseCustomFieldGroupModel.deleteWithRelated();
    });

    this.boards.toModelArray().forEach((boardModel) => {
      boardModel.deleteWithRelated(soft);
    });
  }

  deleteWithRelated(soft) {
    this.deleteRelated(soft);
    this.delete();
  }
}
