import Reflux from 'reflux';

import ProjectActions from 'sentry/actions/projectActions';
import {Project} from 'sentry/types';

type ProjectsStatsStoreInterface = {
  getAll(): ProjectsStatsStoreInterface['itemsBySlug'];

  getBySlug(slug: string): Project;
  getInitialState(): ProjectsStatsStoreInterface['itemsBySlug'];
  itemsBySlug: Record<string, Project>;
  reset(): void;
};

/**
 * This is a store specifically used by the dashboard, so that we can
 * clear the store when the Dashboard unmounts
 * (as to not disrupt ProjectsStore which a lot more components use)
 */
const storeConfig: Reflux.StoreDefinition & ProjectsStatsStoreInterface = {
  itemsBySlug: {},

  init() {
    this.reset();
    this.listenTo(ProjectActions.loadStatsForProjectSuccess, this.onStatsLoadSuccess);
    this.listenTo(ProjectActions.update, this.onUpdate);
    this.listenTo(ProjectActions.updateError, this.onUpdateError);
  },

  getInitialState() {
    return this.itemsBySlug;
  },

  reset() {
    this.itemsBySlug = {};
    this.updatingItems = new Map();
  },

  onStatsLoadSuccess(projects: Project[]) {
    projects.forEach(project => {
      this.itemsBySlug[project.slug] = project;
    });
    this.trigger(this.itemsBySlug);
  },

  /**
   * Optimistic updates
   * @param projectSlug Project slug
   * @param data Project data
   */
  onUpdate(projectSlug: string, data: Project) {
    const project = this.getBySlug(projectSlug);
    this.updatingItems.set(projectSlug, project);
    if (!project) {
      return;
    }

    const newProject: Project = {
      ...project,
      ...data,
    };

    this.itemsBySlug = {
      ...this.itemsBySlug,
      [project.slug]: newProject,
    };
    this.trigger(this.itemsBySlug);
  },

  onUpdateSuccess(data: Project) {
    // Remove project from updating map
    this.updatingItems.delete(data.slug);
  },

  /**
   * Revert project data when there was an error updating project details
   * @param err Error object
   * @param data Previous project data
   */
  onUpdateError(_err: Error, projectSlug: string) {
    const project = this.updatingItems.get(projectSlug);
    if (!project) {
      return;
    }

    this.updatingItems.delete(projectSlug);
    // Restore old project
    this.itemsBySlug = {
      ...this.itemsBySlug,
      [project.slug]: {...project},
    };
    this.trigger(this.itemsBySlug);
  },

  getAll() {
    return this.itemsBySlug;
  },

  getBySlug(slug) {
    return this.itemsBySlug[slug];
  },
};

const ProjectsStatsStore = Reflux.createStore(storeConfig) as Reflux.Store &
  ProjectsStatsStoreInterface;

export default ProjectsStatsStore;