import { Injectable } from '@angular/core';
import { assign, cloneDeep } from 'lodash-es';
import { FuseMockApiService, FuseMockApiUtils } from '@fuse/lib/mock-api';
import {
  filters as filtersData,
  folders as foldersData,
  labels as labelsData,
  mails as mailsData,
  settings as settingsData,
} from 'app/mock-api/apps/mailbox/data';

@Injectable({
  providedIn: 'root',
})
export class MailboxMockApi {
  private _filters: any[] = filtersData;
  private _folders: any[] = foldersData;
  private _mails: any[] = mailsData;
  private _labels: any[] = labelsData;
  private _settings: any = settingsData;

  /**
   * Constructor
   */
  constructor(private _fuseMockApiService: FuseMockApiService) {
    // Register Mock API handlers
    this.registerHandlers();
  }

  // -----------------------------------------------------------------------------------------------------
  // @ Public methods
  // -----------------------------------------------------------------------------------------------------

  /**
   * Register Mock API handlers
   */
  registerHandlers(): void {
    // -----------------------------------------------------------------------------------------------------
    // @ Settings - GET
    // -----------------------------------------------------------------------------------------------------
    this._fuseMockApiService
      .onGet('api/apps/mailbox/settings')
      .reply(() => [200, cloneDeep(this._settings)]);

    // -----------------------------------------------------------------------------------------------------
    // @ Settings - PATCH
    // -----------------------------------------------------------------------------------------------------
    this._fuseMockApiService
      .onPatch('api/apps/mailbox/settings')
      .reply(({ request }) => {
        // Get the settings
        const settings = cloneDeep(request.body.settings);

        // Update the settings
        this._settings = assign({}, this._settings, settings);

        // Return the response
        return [200, cloneDeep(this._settings)];
      });

    // -----------------------------------------------------------------------------------------------------
    // @ Folders - GET
    // -----------------------------------------------------------------------------------------------------
    this._fuseMockApiService.onGet('api/apps/mailbox/folders').reply(() => {
      let count = 0;

      // Iterate through the folders
      this._folders.forEach((folder) => {
        // Get the mails of this folder
        const mails = this._mails.filter(
          (mail: any) => mail.folder === folder.id
        );

        // If we are counting the 'sent' or the 'trash' folder...
        if (folder.slug === 'sent' || folder.slug === 'trash') {
          // Always set the count to 0
          count = 0;
        }
        // If we are counting the 'drafts' or the 'spam' folder...
        else if (
          folder.slug === 'drafts' ||
          folder.slug === 'trash' ||
          folder.slug === 'spam'
        ) {
          // Set the count to the count of all mails
          count = mails.length;
        }
        // Otherwise ('inbox')...
        else {
          // Go through the mails and count the unread ones
          mails.forEach((mail) => {
            if (mail.unread) {
              count++;
            }
          });
        }

        // Append the count to the folder mock-api
        folder.count = count;

        // Reset the count
        count = 0;
      });

      // Return the response
      return [200, cloneDeep(this._folders)];
    });

    // -----------------------------------------------------------------------------------------------------
    // @ Filters - GET
    // -----------------------------------------------------------------------------------------------------
    this._fuseMockApiService
      .onGet('api/apps/mailbox/filters')
      .reply(() => [200, cloneDeep(this._filters)]);

    // -----------------------------------------------------------------------------------------------------
    // @ Labels - GET
    // -----------------------------------------------------------------------------------------------------
    this._fuseMockApiService
      .onGet('api/apps/mailbox/labels')
      .reply(() => [200, cloneDeep(this._labels)]);

    // -----------------------------------------------------------------------------------------------------
    // @ Labels - POST
    // -----------------------------------------------------------------------------------------------------
    this._fuseMockApiService
      .onPost('api/apps/mailbox/label')
      .reply(({ request }) => {
        // Get the label
        const label = cloneDeep(request.body.label);

        // Generate an id
        label.id = FuseMockApiUtils.guid();

        // Generate a slug
        label.slug = label.title
          .toLowerCase()
          .replace(/ /g, '-')
          .replace(/[-]+/g, '-')
          .replace(/[^\w-]+/g, '');

        // Check if the slug is being used and update it if necessary
        const originalSlug = label.slug;

        let sameSlug;
        let slugSuffix = 1;

        do {
          sameSlug = this._labels.filter(
            (item: any) => item.slug === label.slug
          );

          if (sameSlug.length > 0) {
            label.slug = originalSlug + '-' + slugSuffix;
            slugSuffix++;
          }
        } while (sameSlug.length > 0);

        // Add the label
        this._labels.push(label);

        // Return the response
        return [200, label];
      });

    // -----------------------------------------------------------------------------------------------------
    // @ Labels - PATCH
    // -----------------------------------------------------------------------------------------------------
    this._fuseMockApiService
      .onPatch('api/apps/mailbox/label')
      .reply(({ request }) => {
        // Get the id and label
        const id = request.body.id;
        const label = cloneDeep(request.body.label);

        // Prepare the updated label
        let updatedLabel = null;

        // Find the label and update it
        this._labels.forEach((item, index, labels) => {
          if (item.id === id) {
            // Update the slug
            label.slug = label.title
              .toLowerCase()
              .replace(/ /g, '-')
              .replace(/[-]+/g, '-')
              .replace(/[^\w-]+/g, '');

            // Update the label
            labels[index] = assign({}, labels[index], label);

            // Store the updated label
            updatedLabel = labels[index];
          }
        });

        // Return the response
        return [200, updatedLabel];
      });

    // -----------------------------------------------------------------------------------------------------
    // @ Labels - DELETE
    // -----------------------------------------------------------------------------------------------------
    this._fuseMockApiService
      .onDelete('api/apps/mailbox/label')
      .reply(({ request }) => {
        // Get the id
        const id = request.params.get('id');

        // Find the label and delete it
        const index = this._labels.findIndex((item: any) => item.id === id);
        this._labels.splice(index, 1);

        // Get all the mails that have the label
        const mailsWithLabel = this._mails.filter(
          (mail: any) => mail.labels.indexOf(id) > -1
        );

        // Iterate through them and remove the label
        mailsWithLabel.forEach((mail) => {
          mail.labels.splice(mail.labels.indexOf(id), 1);
        });

        // Return the response
        return [200, true];
      });

    // -----------------------------------------------------------------------------------------------------
    // @ Mails - GET
    // -----------------------------------------------------------------------------------------------------
    this._fuseMockApiService
      .onGet('api/apps/mailbox/mails', 625)
      .reply(({ request }) => {
        // First, decide if mails are requested by folder, filter or label
        const byFolder = request.params.get('folder');
        const byFilter = request.params.get('filter');
        const byLabel = request.params.get('label');

        // Clone the mails mock-api to prevent accidental mock-api updates
        let mails: any[] | null = cloneDeep(this._mails);

        // Filter the mails depending on the requested by type
        mails = mails.filter((mail) => {
          if (byFolder) {
            return (
              mail.folder ===
              this._folders.find((folder: any) => folder.slug === byFolder).id
            );
          }

          if (byFilter) {
            return mail[byFilter] === true;
          }

          if (byLabel) {
            return mail.labels.includes(
              this._labels.find((label: any) => label.slug === byLabel).id
            );
          }
        });

        // Sort by date - descending
        mails.sort(
          (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
        );

        // Figure out the cc and bcc counts
        mails.forEach((mail) => {
          mail.ccCount = mail.cc ? mail.cc.length : 0;
          mail.bccCount = mail.bcc ? mail.bcc.length : 0;
        });

        // Paginate - Start
        const mailsLength = mails.length;
        const resultsPerPage = 10;

        // Get the requested page number
        const page = parseInt(request.params.get('page') ?? '1', 10);

        // Calculate pagination details
        const begin = (page - 1) * resultsPerPage;
        const end = Math.min(resultsPerPage * page, mailsLength);
        const lastPage = Math.max(Math.ceil(mailsLength / resultsPerPage), 1);

        // Prepare the pagination object
        let pagination = {};

        // If the requested page number is bigger than
        // the last possible page number, return null for
        // mails but also send the last possible page so
        // the app can navigate to there
        if (page > lastPage) {
          mails = null;
          pagination = {
            lastPage,
          };
        } else {
          // Paginate the results by 10
          mails = mails.slice(begin, end);

          // Prepare the pagination mock-api
          pagination = {
            totalResults: mailsLength,
            resultsPerPage: resultsPerPage,
            currentPage: page,
            lastPage: lastPage,
            startIndex: begin,
            endIndex: end - 1,
          };
        }

        // Return the response
        return [
          200,
          {
            mails,
            pagination,
          },
        ];
      });

    // -----------------------------------------------------------------------------------------------------
    // @ Mail - GET
    // -----------------------------------------------------------------------------------------------------
    this._fuseMockApiService
      .onGet('api/apps/mailbox/mail')
      .reply(({ request }) => {
        // Get the id from the params
        const id = request.params.get('id');

        // Clone the mails mock-api to prevent accidental mock-api updates
        const mails = cloneDeep(this._mails);

        // Find the mail
        const mail = mails.find((item: any) => item.id === id);

        return [200, mail];
      });

    // -----------------------------------------------------------------------------------------------------
    // @ Mail - PATCH
    // -----------------------------------------------------------------------------------------------------
    this._fuseMockApiService
      .onPatch('api/apps/mailbox/mail')
      .reply(({ request }) => {
        // Get the id and mail
        const id = request.body.id;
        const mail = cloneDeep(request.body.mail);

        // Prepare the updated mail
        let updatedMail = null;

        // Find the mail and update it
        this._mails.forEach((item, index, mails) => {
          if (item.id === id) {
            // Update the mail
            mails[index] = assign({}, mails[index], mail);

            // Store the updated mail
            updatedMail = mails[index];
          }
        });

        // Return the response
        return [200, updatedMail];
      });
  }
}
