/* eslint-disable @typescript-eslint/no-inferrable-types */
/* eslint-disable prettier/prettier */
import { Body, Controller, Get, Param, Post, Res } from "@nestjs/common";
import { QueryBus } from "@nestjs/cqrs";
import { Request, Response } from 'express';
import { MessagePattern, Transport } from "@nestjs/microservices";
import { catchError, Observable, Observer, of } from "rxjs";
import { FisAppQuery } from "../FisAppQueryMicroserviceModule/queries/fisApp/fisApp.query";
import { PlantationSiteQuery } from "../PlantationSiteOperationMicroserviceModule/queries/plantationSite/plantationSite.query";
import { SubscriberProxy } from "../FisAppQueryMicroserviceModule/SubscriberProxy";
import { HttpService } from "@nestjs/axios";
import { FisCreateMessageUtility, QueryMessage, NotificationData, NotificationMessage, Query, ResponseMessage, Uuid } from '../CommonMicroserviceModule/message/FISAppMessageJSUtility/interface/export'
import { GraphQLProviderManager, ServiceProviderData } from '../CommonMicroserviceModule/provider/Fis/interface/export';

const io = require("socket.io-client");
import { Socket } from "socket.io-client";

import { IPs } from "./GetIPAddress";
import { AxiosRequestConfig } from "axios";
@Controller("api")
export class QueryClientController {

  static result;
  ResponseService: FisCreateMessageUtility;
  QueryService: FisCreateMessageUtility;
  provider1 = new GraphQLProviderManager();
  constructor(
    private readonly queryBus: QueryBus,
    private http: HttpService
  ) {
    this.registerServiceProvider();

  }
  public MessageService: FisCreateMessageUtility = new FisCreateMessageUtility("FisAppID/Name");


  //request from microservice : QUERY command
  @MessagePattern({ cmd: 'QUERY' }, Transport.TCP)
  query(request: QueryMessage): Observable<ResponseMessage> {

    return this.requestSubscriber(request);
  }
  @Post('/defaultObservable')
  defaultObservable(@Body() req: QueryMessage, @Res() res: Response) {
    res.setHeader("Content-Type", "application/json");

    this.requestSubscriber(req).subscribe(
      {
        next: (msg) => {
          res.write(JSON.stringify([msg])); // New message
        },
        error: (err) => {
          res.write(JSON.stringify(err.message, null, 4)); // This need to fisapp error message
          res.end(); // Close/complete the message.
        },
        complete: () => { res.end() } // Close/complete the message.
      }
    )

    // return this.requestSubscriber(req);
  }


  //subscribe and response the data from graphql and subscriberProxy response Observable
  requestSubscriber(request: any): Observable<ResponseMessage> {

    //instantiate the request and response service
    if (!this.ResponseService) {
      this.ResponseService = new FisCreateMessageUtility('getResponseMessage');
      this.QueryService = new FisCreateMessageUtility('getQueryMessage');
    }
    let queryMessage: QueryMessage = request;
    let requestPayload: FisAppQuery | PlantationSiteQuery;

    // if (request.data['type'] === 'dataservice') {
    requestPayload = new FisAppQuery(request);
    // } else {
    //   requestPayload = new PlantationSiteQuery(request);
    // }

    //begin prototype
    let observer: Observer<any>;

    const observableResult: Observable<any> = new Observable<any>((localObserver) => {
      observer = localObserver;

      if (process.env.ACT_AS_SUPERGRAPH === "Yes") {
        let providerLists = [];

        this.provider1.getActiveProviders().forEach((provider: ServiceProviderData) => { //get the actual provider lists
          if (provider.name.indexOf("QueryModule") > -1 && provider.name.indexOf(process.env.SERVERNAME) === -1 && provider.name.indexOf('undefined') === -1) {

            providerLists.push(provider);
          }
        })
        const providerListsCount = providerLists.length
        let completeCount = 0;

        console.log(providerLists)

        providerLists.forEach((provider: ServiceProviderData) => { //based on actual provider list subscribe the data

          const http_config: AxiosRequestConfig = {
            responseType: "stream"
          };

          let tempBuffer: string = "";

          http_config.method = "post";
          http_config.url = provider.url + '/api/defaultObservable';
          http_config.data = request;

          this.http.axiosRef.request(http_config).then((res) => {

            res.data.on('data', buffer => {

              let x = buffer.toString();


              try {

                // Attempt to parse 
                let temp = tempBuffer + x;
                temp = JSON.parse(temp);

                // Array conversion if needed
                if (temp[0]) {
                  temp = temp[0];
                }
                // Other operation
                // console.log(provider.url)
                observer.next(temp);

                // Reset buffer
                tempBuffer = "";
              }
              catch (e) {
                console.count("Error")
                // Else, something is wrong so add to buffer
                // console.log(e.message);
                // console.log(data);
                tempBuffer = tempBuffer + x;
              }
            })

            res.data.on('error', err => {
              completeCount = completeCount + 1;
              // console.log(JSON.stringify(err.message, null, 4));
              if (completeCount == providerListsCount) {
                // console.log("completed all ");
                observer.complete();
              }
            })

            res.data.on('end', () => {
              completeCount = completeCount + 1;
              if (completeCount == providerListsCount) {
                // console.log('completed all ');
                observer.complete();
              }
            })
          })

        })



      } else { // if not act as supergraph , work as usual.

        // return new Observable((subscriber) => {
        SubscriberProxy.subscribeCommonServiceChannel(this.http, request)
          .pipe(
            catchError(err => {
              console.log("error: " + err);
              observer.error(err)
              return of(err)
            })
          )
          .subscribe({
            next: (x) => {

              if (x.__typename == 'SummaryResponse') {
                let summaryMsg = this.ResponseService.getResponseSummaryMessage('SubscriberProxy FisApp response', x, queryMessage)
                summaryMsg.header.messageProducerInformation.origin.programName = process.env.SERVERNAME
                observer.next(summaryMsg)
              } else if (x.__typename == 'StatusResponse') {
                let statusMsg = this.ResponseService.getResponseStatusMessage('SubscriberProxy FisApp response', x, queryMessage, x.status)
                statusMsg.header.messageProducerInformation.origin.programName = process.env.SERVERNAME
                observer.next(statusMsg)
              } else {
                let responseMsg = this.ResponseService.getResponseDataMessage('SubscriberProxy FisApp response', x, queryMessage)
                responseMsg.header.messageProducerInformation.origin.programName = process.env.SERVERNAME
                observer.next(responseMsg)
              }


            },
            error: (e) => {
              observer.error(e)
            },
            complete: () => {
              observer.complete()
            }
          })
        // })
      }




    })

    return observableResult;




  }

  //for fisApp
  @Post('/request')
  request(@Body() req) {
    return this.requestCmd('QUERY', req);
  }

  @Post('/default')
  default(@Body() req: QueryMessage) {

    return this.requestCmdRequest('QUERY', req);
  }


  //generate params to fisApp request message
  requestCmd(cmd, params = {}) {

    if (!this.MessageService) {
      this.MessageService = new FisCreateMessageUtility('getQueryMessage');
    }


    return this.requestCmdRequest(cmd, this.MessageService.getQueryMessage("ABC", Query.GetData, params));
  }


  //return PROMISE
  requestCmdRequest(cmd = null, params: QueryMessage) {
    const pattern = { cmd: cmd };
    let storearray = Array();

    if (!this.MessageService) {
      this.MessageService = new FisCreateMessageUtility('getQueryMessage');
    }



    let newPromise = new Promise((resolve, reject) => {
      this.requestSubscriber(
        params
      )
        .subscribe({
          next: (x) => {
            storearray.push(JSON.stringify(x, null, 10));

          },
          error: (err) => {
            reject(err)
          },
          complete: () => {
            console.log("completed")
            resolve(storearray.join(','));
          },
        });
    });

    return newPromise;
  }

  registerServiceProvider() {

    if (!this.MessageService) {
      this.MessageService = new FisCreateMessageUtility('getLoginMessage');
    }
    let provider1_Id = "Provider1";

    const serverStarted1: NotificationData = {
      type: "ServerStarted",
      message: "Server has started",
      appId: provider1_Id,
      url: "url1"
    };

    const messageUtil1: FisCreateMessageUtility = new FisCreateMessageUtility(provider1_Id);
    let requestMessage = this.MessageService.getLoginMessage();
    const newNotificationMessage1: NotificationMessage = messageUtil1.getNotificationMessage(generateId(), serverStarted1, "New Notification");

    this.provider1.processNotification(newNotificationMessage1);


    requestMessage.data["serviceProvider"] = {
      "name": "QueryModule - " + process.env.SERVERNAME,
      "instanceId": generateId(),
      "secret": "FisSecret",
      "singleton": true,
      "url": "http://" + IPs['v4'] + ":" + process.env.QueryModuleUrlPort
    }

    let timer;


    let socket: Socket;
    let observer: Observer<any>;

    const observableResult: Observable<ResponseMessage> = new Observable<any>((localObserver) => {
      observer = localObserver;
    })
    observableResult.subscribe(
      {
        next: (x) => {
          console.count("This response(start2)");
          const message = JSON.parse(x['message']);

          message.data.ServiceProvidersData?.providers.map(x => {

            this.provider1.addGraphQLProvider(x.id, x)


          })
          // console.log(this.provider1.getActiveProviders());

          console.count("This response(end2)");
        },
        error: (err) => {
          console.log("error");
        },
        complete: () => {
          console.log("complete");
        }
      }
    )


    socket = new io(process.env.PLAYGROUND_PATH, { autoConnect: true, auth: {}, query: {} })
    console.log(process.env.PLAYGROUND_PATH)

    socket.on("connect", () => {
      console.log("Client connected.(start2)")
    })

    socket.on('response', (res: any) => {
      if (res.id > "") {
        if (!res.complete) observer.next(res);
        else if (res.complete) observer.complete();
      }
      else {
        console.log("Connection error");
        console.log(res);
      }
    })

    const registerProviderConnnection = () => {
      this.http.post(process.env.UCP_HOST, requestMessage).subscribe({
        next: (response) => {
          if (response.data[0]?.data?.ServerUCP?.ucpId == undefined) {

            clearInterval(timer)
            console.count("Error getting ucpId - " + response.data[0]?.data?.ServerUCP?.ucpId)
            timer = setInterval(registerProviderConnnection, parseInt(process.env.FAILED_TIMEOUT))
          } else {
            let msg = messageUtil1.getSubscribeNotifMessage(response.data[0].data.ServerUCP.ucpId, "serviceprovider");

            if (socket && socket.connected) {
              // socket already connected. "connect" event already started
              // perform emit
              socket.emit('request', msg);
            }
            else {
              // socket not yet connected. "connect" event not yet started
              socket.on("connect", () => {
                // Add a scheduled emit
                socket.emit('request', msg);
              })
            }

            clearInterval(timer)
          }
        },
        error: (error) => {
          clearInterval(timer)
          console.log(error);
          timer = setInterval(registerProviderConnnection, parseInt(process.env.FAILED_TIMEOUT))
        },
        complete: () => {
          console.log("Completed 1");
          clearInterval(timer)
        }
      });
    }
    timer = registerProviderConnnection()
  }


}
function generateId(): string {
  let messageId = new Uuid();
  return messageId.generateId().toString();
}