/*
 * Copyright (C) 2018 StApps
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */
import {
  SCBulkAddResponse,
  SCBulkAddRoute,
  SCBulkDoneResponse,
  SCBulkDoneRoute,
  SCBulkResponse,
  SCBulkRoute,
  SCSearchRequest,
  SCSearchResponse,
  SCSearchRoute,
  SCThingType,
} from '@openstapps/core';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import chaiSpies from 'chai-spies';
import {ApiError, HttpClient, HttpClientRequest, HttpClientResponse} from '@openstapps/api';
import {copy} from '../src/copy.js';

/**
 * Recursive Partial
 *
 * @see https://stackoverflow.com/a/51365037
 */
export type RecursivePartial<T> = {
  [P in keyof T]?: T[P] extends Array<infer U>
    ? Array<RecursivePartial<U>>
    : T[P] extends object
    ? RecursivePartial<T[P]>
    : T[P];
};

chai.should();
chai.use(chaiSpies);
chai.use(chaiAsPromised);

const sandbox = chai.spy.sandbox();

const bulkRoute = new SCBulkRoute();
const bulkAddRoute = new SCBulkAddRoute();
const bulkDoneRoute = new SCBulkDoneRoute();
const searchRoute = new SCSearchRoute();

const httpClient = new HttpClient();

describe('Copy', function () {
  afterEach(function () {
    sandbox.restore();
  });

  it('should copy', async function () {
    type responses = HttpClientResponse<
      SCBulkAddResponse | SCBulkDoneResponse | SCBulkResponse | SCSearchResponse
    >;

    sandbox.on(
      httpClient,
      'request',
      async (request: HttpClientRequest): Promise<RecursivePartial<responses>> => {
        if (request.url.toString() === 'http://foo.bar' + searchRoute.getUrlPath().toString()) {
          const body = request.body as SCSearchRequest;

          let count = 0;
          if (typeof body.size === 'number' && body.size > 0) {
            count = 1;
          }

          return {
            body: {
              data: [
                {
                  categories: ['main dish'],
                  name: 'foobar',
                  origin: {
                    indexed: new Date(Date.now()).toISOString(),
                    name: 'bar',
                  },
                  type: SCThingType.Dish,
                  uid: 'foo',
                },
              ],
              facets: [],
              pagination: {
                count: count,
                offset: 0,
                total: 1,
              },
              stats: {
                time: 1,
              },
            },
            statusCode: searchRoute.statusCodeSuccess,
          };
        } else if (request.url.toString() === 'http://localhost' + bulkRoute.getUrlPath().toString()) {
          return {
            body: {
              state: 'in progress',
              uid: 'foo',
            },
            statusCode: bulkRoute.statusCodeSuccess,
          };
        } else if (
          request.url.toString() ===
          'http://localhost' +
            bulkAddRoute
              .getUrlPath({
                UID: 'foo',
              })
              .toString()
        ) {
          return {
            body: {},
            statusCode: bulkAddRoute.statusCodeSuccess,
          };
        }

        return {
          body: {},
          statusCode: bulkDoneRoute.statusCodeSuccess,
        };
      },
    );

    await copy(httpClient, {
      batchSize: 5,
      from: 'http://foo.bar',
      source: 'stapps-copy',
      to: 'http://localhost',
      type: SCThingType.Dish,
      version: 'foo.bar.foobar',
    });
  });

  it('should fail to copy', async function () {
    type responses = HttpClientResponse<
      SCBulkAddResponse | SCBulkDoneResponse | SCBulkResponse | SCSearchResponse
    >;

    sandbox.on(
      httpClient,
      'request',
      async (request: HttpClientRequest): Promise<RecursivePartial<responses>> => {
        if (request.url.toString() === 'http://foo.bar' + searchRoute.getUrlPath().toString()) {
          const body = request.body as SCSearchRequest;

          if (typeof body.size === 'number' && body.size > 0) {
            throw new ApiError({});
          }

          return {
            body: {
              data: [
                {
                  categories: ['main dish'],
                  name: 'foobar',
                  origin: {
                    indexed: new Date(Date.now()).toISOString(),
                    name: 'bar',
                  },
                  type: SCThingType.Dish,
                  uid: 'foo',
                },
              ],
              facets: [],
              pagination: {
                count: 0,
                offset: 0,
                total: 1,
              },
              stats: {
                time: 1,
              },
            },
            statusCode: searchRoute.statusCodeSuccess,
          };
        } else if (request.url.toString() === 'http://localhost' + bulkRoute.getUrlPath().toString()) {
          return {
            body: {
              state: 'in progress',
              uid: 'foo',
            },
            statusCode: bulkRoute.statusCodeSuccess,
          };
        } else if (
          request.url.toString() ===
          'http://localhost' +
            bulkAddRoute
              .getUrlPath({
                UID: 'foo',
              })
              .toString()
        ) {
          return {
            body: {},
            statusCode: bulkAddRoute.statusCodeSuccess,
          };
        }

        return {
          body: {},
          statusCode: bulkDoneRoute.statusCodeSuccess,
        };
      },
    );

    await copy(httpClient, {
      batchSize: 5,
      from: 'http://foo.bar',
      source: 'stapps-copy',
      to: 'http://localhost',
      type: SCThingType.Dish,
      version: 'foo.bar.foobar',
    }).should.be.rejectedWith(ApiError);
  });
});
