/**
 * This file is part of the NocoBase (R) project.
 * Copyright (c) 2020-2024 NocoBase Co., Ltd.
 * Authors: NocoBase Team.
 *
 * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
 * For more information, please refer to: https://www.nocobase.com/agreement.
 */

import { prepareApp } from './prepare';
import { Database } from '@nocobase/database';
import { MockServer } from '@nocobase/test';

describe('adjacency list repository', () => {
  let app: MockServer;
  let db: Database;

  beforeEach(async () => {
    app = await prepareApp();
    db = app.db;
    await db.clean({ drop: true });
  });

  afterEach(async () => {
    await app.destroy();
  });

  it('should append relation parent recursively with belongs to assoc', async () => {
    const Category = db.collection({
      name: 'categories',
      tree: 'adjacency-list',
      fields: [
        { type: 'string', name: 'name' },
        {
          type: 'belongsTo',
          name: 'parent',
          treeParent: true,
        },
        {
          type: 'hasMany',
          name: 'children',
          treeChildren: true,
        },
      ],
    });

    const Post = db.collection({
      name: 'posts',
      fields: [
        { type: 'string', name: 'title' },
        { type: 'belongsTo', name: 'category', target: 'categories', foreignKey: 'categoryId' },
        { type: 'belongsTo', name: 'category2', target: 'categories', foreignKey: 'categoryId2' },
        { type: 'belongsTo', name: 'category3', target: 'categories', foreignKey: 'categoryId3' },
      ],
    });

    await db.sync();

    await Category.repository.create({
      values: [
        {
          name: 'c1',
          children: [
            {
              name: 'c1-1',
              children: [
                {
                  name: 'c1-1-1',
                },
              ],
            },
            {
              name: 'c12',
            },
          ],
        },
      ],
    });

    const c111 = await Category.repository.findOne({ where: { name: 'c1-1-1' } });

    const p1 = await Post.repository.create({
      values: [
        {
          title: 'p1',
          category: { id: c111.id },
          category2: null,
          category3: { id: c111.id },
        },
      ],
    });

    const p1WithCategory = await Post.repository.findOne({
      appends: [
        'category',
        'category.parent(recursively=true)',
        'category2',
        'category2.parent(recursively=true)',
        'category3',
        'category3.parent(recursively=true)',
      ],
    });

    expect(p1WithCategory.category.parent.name).toBe('c1-1');
    expect(p1WithCategory.category.parent.parent.name).toBe('c1');
    expect(p1WithCategory.category2).toBeNull();
    expect(p1WithCategory.category3.parent.name).toBe('c1-1');
    expect(p1WithCategory.category3.parent.parent.name).toBe('c1');
  });

  it('should append relation parent recursively with belongs to many', async () => {
    const Category = db.collection({
      name: 'categories',
      tree: 'adjacency-list',
      fields: [
        { type: 'string', name: 'name' },
        {
          type: 'belongsTo',
          name: 'parent',
          treeParent: true,
        },
        {
          type: 'hasMany',
          name: 'children',
          treeChildren: true,
        },
      ],
    });

    const Post = db.collection({
      name: 'posts',
      fields: [
        { type: 'string', name: 'title' },
        { type: 'belongsToMany', name: 'categories', target: 'categories', through: 'table1' },
        { type: 'belongsToMany', name: 'categories2', target: 'categories', through: 'table2' },
        { type: 'belongsToMany', name: 'categories3', target: 'categories', through: 'table3' },
      ],
    });

    await db.sync();

    await Category.repository.create({
      values: [
        {
          name: 'c1',
          children: [
            {
              name: 'c1-1',
              children: [
                {
                  name: 'c1-1-1',
                },
              ],
            },
            {
              name: 'c12',
            },
          ],
        },
      ],
    });

    const c111 = await Category.repository.findOne({ where: { name: 'c1-1-1' } });

    const p1 = await Post.repository.create({
      values: [
        {
          title: 'p1',
          categories: [{ id: c111.id }],
          categories2: [{ id: c111.id }],
          categories3: [],
        },
      ],
    });

    const p1WithCategory = await Post.repository.findOne({
      appends: [
        'categories',
        'categories.parent(recursively=true)',
        'categories2',
        'categories2.parent(recursively=true)',
        'categories3',
        'categories3.parent(recursively=true)',
      ],
    });

    expect(p1WithCategory.categories[0].parent.name).toBe('c1-1');
    expect(p1WithCategory.categories[0].parent.parent.name).toBe('c1');

    expect(p1WithCategory.categories2[0].parent.name).toBe('c1-1');
    expect(p1WithCategory.categories2[0].parent.parent.name).toBe('c1');
  });

  it('should append parent recursively', async () => {
    const Tree = db.collection({
      name: 'categories',
      tree: 'adjacency-list',
      fields: [
        { type: 'string', name: 'name' },
        {
          type: 'belongsTo',
          name: 'parent',
          treeParent: true,
        },
        {
          type: 'hasMany',
          name: 'children',
          treeChildren: true,
        },
      ],
    });

    await db.sync();

    await Tree.repository.create({
      values: [
        {
          name: 'c1',
          children: [
            {
              name: 'c1-1',
              children: [
                {
                  name: 'c1-1-1',
                },
              ],
            },
            {
              name: 'c12',
            },
          ],
        },
      ],
    });

    const c111 = await Tree.repository.findOne({ where: { name: 'c1-1-1' }, appends: ['parent(recursively=true)'] });
    expect(c111.parent.name).toBe('c1-1');
    expect(c111.parent.parent.name).toBe('c1');
  });
});
