text: import ORM from "../ORM";
  import Model from "../Model";
  import { fk, many, oneToOne, attr } from "../fields";
  
  /**
   * These utils create a database schema for testing.
   * The schema is simple but covers most relational
   * cases: foreign keys, one-to-ones, many-to-many's,
   * named reverse relations.
   */
  
  const AUTHORS_INITIAL = [
      {
          name: "Tommi Kaikkonen",
      },
      {
          name: "John Doe",
      },
      {
          name: "Stephen King",
      },
  ];
  
  const COVERS_INITIAL = [
      {
          src: "cover.jpg",
      },
      {
          src: "cover.jpg",
      },
      {
          src: "cover.jpg",
      },
  ];
  
  const GENRES_INITIAL = [
      {
          name: "Biography",
      },
      {
          name: "Autobiography",
      },
      {
          name: "Software Development",
      },
      {
          name: "Redux",
      },
  ];
  
  const TAGS_INITIAL = [
      {
          name: "Technology",
      },
      {
          name: "Literary",
      },
      {
          name: "Natural",
      },
      {
          name: "Redux",
      },
  ];
  
  const BOOKS_INITIAL = [
      {
          name: "Tommi Kaikkonen - an Autobiography",
          author: 0,
          cover: 0,
          genres: [0, 1],
          tags: ["Technology", "Literary"],
          releaseYear: 2050,
          publisher: 1,
      },
      {
          name: "Clean Code",
          author: 1,
          cover: 1,
          genres: [2],
          tags: ["Technology"],
          releaseYear: 2008,
          publisher: 0,
      },
      {
          name: "Getting Started with Redux",
          author: 2,
          cover: 2,
          genres: [2, 3],
          tags: ["Technology", "Redux"],
          releaseYear: 2015,
          publisher: 0,
      },
  ];
  
  const PUBLISHERS_INITIAL = [
      {
          name: "Technical Publishing",
      },
      {
          name: "Autobiographies Inc",
      },
      {
          name: "Paramount Pictures",
      },
  ];
  
  const MOVIES_INITIAL = [
      {
          name: "The Godfather",
          characters: ["Vito Corleone", "Tom Hagen", "Bonasera"],
          hasPremiered: true,
          rating: 9.2,
          meta: {},
          publisherId: 2,
      },
  ];
  
  export function createTestModels() {
      const Book = class BookModel extends Model {
          static get fields() {
              return {
                  id: attr(),
                  name: attr(),
                  releaseYear: attr(),
                  author: fk("Author", "books"),
                  cover: oneToOne("Cover"),
                  genres: many("Genre", "books"),
                  tags: many("Tag", "books"),
                  publisher: fk("Publisher", "books"),
              };
          }
      };
      Book.modelName = "Book";
  
      const Author = class AuthorModel extends Model {
          static get fields() {
              return {
                  id: attr(),
                  name: attr(),
                  publishers: many({
                      to: "Publisher",
                      through: "Book",
                      relatedName: "authors",
                  }),
              };
          }
      };
      Author.modelName = "Author";
  
      const Cover = class CoverModel extends Model {};
      Cover.modelName = "Cover";
      Cover.fields = {
          id: attr(),
          src: attr(),
      };
  
      const Genre = class GenreModel extends Model {};
      Genre.modelName = "Genre";
      Genre.fields = {
          id: attr(),
          name: attr(),
      };
  
      const Tag = class TagModel extends Model {};
      Tag.modelName = "Tag";
      Tag.options = {
          idAttribute: "name",
      };
      Tag.fields = {
          name: attr(),
          subTags: many("this", "parentTags"),
          // TODO: bidirectional many-to-many relations
          // synonymousTags: many('Tag', 'synonymousTags'),
      };
  
      const Publisher = class PublisherModel extends Model {};
      Publisher.modelName = "Publisher";
      Publisher.fields = {
          id: attr(),
          name: attr(),
      };
  
      const Movie = class MovieModel extends Model {};
      Movie.modelName = "Movie";
      Movie.fields = {
          id: attr(),
          name: attr(),
          rating: attr(),
          hasPremiered: attr(),
          characters: attr(),
          meta: attr(),
          publisherId: fk({
              to: "Publisher",
              as: "publisher",
              relatedName: "movies",
          }),
      };
  
      return {
          Book,
          Author,
          Cover,
          Genre,
          Tag,
          Publisher,
          Movie,
      };
  }
  
  export function createTestORM(customModels) {
      const models = customModels || createTestModels();
      const { Book, Author, Cover, Genre, Tag, Publisher, Movie } = models;
  
      const orm = new ORM();
      orm.register(Book, Author, Cover, Genre, Tag, Publisher, Movie);
      return orm;
  }
  
  export function createTestSession() {
      const orm = createTestORM();
      return orm.session(orm.getEmptyState());
  }
  
  export function createTestSessionWithData(customORM) {
      const orm = customORM || createTestORM();
      const state = orm.getEmptyState();
      const {
          Author,
          Cover,
          Genre,
          Tag,
          Book,
          Publisher,
          Movie,
      } = orm.mutableSession(state);
  
      AUTHORS_INITIAL.forEach((props) => Author.create(props));
      COVERS_INITIAL.forEach((props) => Cover.create(props));
      GENRES_INITIAL.forEach((props) => Genre.create(props));
      TAGS_INITIAL.forEach((props) => Tag.create(props));
      BOOKS_INITIAL.forEach((props) => Book.create(props));
      PUBLISHERS_INITIAL.forEach((props) => Publisher.create(props));
      MOVIES_INITIAL.forEach((props) => Movie.create(props));
  
      const normalSession = orm.session(state);
      return { session: normalSession, orm, state };
  }
  
  export const isSubclass = (a, b) => a.prototype instanceof b;
  
  export const measureMsSince = (startTime) => {
      if (!startTime) {
          return process.hrtime();
      }
      const endTime = process.hrtime(startTime);
      return Math.round(endTime[0] * 1000 + endTime[1] / 1000000);
  };
  
  export const nTimes = (n) => Array.from({ length: n });
  
  export function measureMs(fn) {
      const start = measureMsSince();
      fn(...arguments);
      return measureMsSince(start);
  }
  
  export const avg = (arr) => {
      if (arr.length === 0) return null;
      const sum = arr.reduce((cur, summand) => cur + summand);
      return sum / arr.length;
  };
  
  export const round = (num, precision, base = 10) => {
      const precisionFactor = base ** precision;
      return Math.round(num * precisionFactor) / precisionFactor;
  };
  