/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { Inject, Injectable } from '@angular/core';

import { NodeItem, NodeList, TRASH_FOLDER_ID_TOKEN } from '@zeppelin/interfaces';
import { NotesInfoItem } from '@zeppelin/sdk';

import { ArrayOrderingService } from './array-ordering.service';

@Injectable({
  providedIn: 'root'
})
export class NoteListService {
  notes: NodeList = {
    root: { children: [] },
    flatList: [],
    flatFolderMap: {}
  };

  setNotes(notesList: NotesInfoItem[]) {
    // a flat list to boost searching
    this.notes.flatList = notesList.map(note => {
      const isTrash = note.path ? note.path.split('/')[1] === this.TRASH_FOLDER_ID : false;
      return { ...note, isTrash };
    });

    // construct the folder-based tree
    this.notes.root = { children: [] };
    this.notes.flatFolderMap = {};
    notesList.reduce((root, note) => {
      const notePath = note.path || note.id;
      const nodes = notePath.match(/([^\/][^\/]*)/g)!;

      // recursively add nodes
      this.addNode(root, nodes, note.id);

      return root;
    }, this.notes.root);
    this.notes.root.children.sort(this.arrayOrderingService.noteComparator);
  }

  addNode(curDir: { id?: string; children: NodeItem[] }, nodes: RegExpMatchArray, noteId: string) {
    if (nodes && nodes.length === 1) {
      // the leaf
      curDir.children.push({
        id: noteId,
        key: noteId,
        title: nodes[0],
        isLeaf: true,
        nodeType: 'note',
        path: curDir.id ? `${curDir.id}/${nodes[0]}` : nodes[0],
        isTrash: curDir.id ? curDir.id.split('/')[0] === this.TRASH_FOLDER_ID : false
      });
    } else {
      // a folder node
      const node = nodes.shift()!;
      const dir = curDir.children.filter(
        (c): c is NodeItem & { title: string; children: NodeItem[] } => c.title === node && c.children !== undefined
      )[0];
      if (dir !== undefined) {
        // found an existing dir
        this.addNode(dir, nodes, noteId);
      } else {
        const id = curDir.id ? `${curDir.id}/${node}` : node;
        const newDir = {
          id,
          key: id,
          title: node,
          expanded: false,
          nodeType: id === this.TRASH_FOLDER_ID ? 'trash' : 'folder',
          children: [],
          isTrash: curDir.id ? curDir.id.split('/')[0] === this.TRASH_FOLDER_ID : false
        };

        // add the folder to flat folder map
        this.notes.flatFolderMap[newDir.id] = newDir;

        curDir.children.push(newDir);
        this.addNode(newDir, nodes, noteId);
      }
    }
  }

  constructor(
    @Inject(TRASH_FOLDER_ID_TOKEN) public TRASH_FOLDER_ID: string,
    private arrayOrderingService: ArrayOrderingService
  ) {}
}
