/* eslint-disable @typescript-eslint/no-inferrable-types */
/* eslint-disable prettier/prettier */
import { Body, Controller, Post, Res } from "@nestjs/common";
import { Response } from 'express';
import { MessagePattern, Transport } from "@nestjs/microservices";
import { catchError, Observable, Observer, of } from "rxjs";
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';
import { IPs } from "./GetIPAddress";
import { AxiosRequestConfig } from "axios";
import { DomainProxyController } from "../CommonMicroserviceModule/clients/DomainProxy/src/services/DP.controller";
import { ConnectionInterface } from "../CommonMicroserviceModule/clients/DomainProxy/src/interface/DP.Connection.interface";
import {
  DatabaseNotificationData,
  DataTypeFormat,
  NotificationMicroserviceData,
} from '../CommonMicroserviceModule/clients/DomainProxy/src/message/FISAppMessageJSUtility/interface/export';

@Controller("api")
export class QueryClientController {

  static result;
  ResponseService: FisCreateMessageUtility;
  QueryService: FisCreateMessageUtility;
  static provider1 = new GraphQLProviderManager();
  constructor(
    // private readonly queryBus: QueryBus,
    private http: HttpService,
    private DPControllerInstance: DomainProxyController
  ) {

    this.DPControllerInstance.setApplicationName("Testing domain proxy");

    this.registerServiceProvider(this.DPControllerInstance);
    this.registerNotification(this.DPControllerInstance);


  }
  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');
    }
    const 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") {
        const providerLists = [];

        QueryClientController.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 = process.env.UCP_HOST_PATH + '/serviceprovider';

          http_config.headers = {
            "Content-Type": "application/json",
            "Access-Control-Allow-Origin": "*"
          };


          // http_config.data = request;
          http_config.data = JSON.stringify({
            // id: provider.id,
            name: provider.name,
            path: "/api/defaultObservable",
            req: request
          });


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

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

              const 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') {
                const 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') {
                const statusMsg = this.ResponseService.getResponseStatusMessage('SubscriberProxy FisApp response', x, queryMessage, x.status)
                statusMsg.header.messageProducerInformation.origin.programName = process.env.SERVERNAME
                observer.next(statusMsg)
              } else {

                const 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 };
    const storearray = [];

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



    const 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;
  }

  registerNotification(DPControllerInstance: DomainProxyController) {

    if (!this.MessageService) {
      this.MessageService = new FisCreateMessageUtility('getLoginMessage');
    }
    const provider1_Id = "Provider1";
    const appname: string = "Provider Application";
    let currentUCPId: string = "";

    const msg = DPControllerInstance.getMessageService().getLoginMessage();

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

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

    QueryClientController.provider1.processNotification(newNotificationMessage1);

    const settings: ConnectionInterface = {
      IdName: appname,
      Description: "Testing login",
      Type: "Http",
      Target: "http://swopt.com:3011",
    }
    DPControllerInstance.setConnection(settings)

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

    let timer;

    const registerProviderConnnection = () => {

      DPControllerInstance.setIsConnectionStatusObserved(appname, true);
      DPControllerInstance.send(appname, msg).subscribe({
        next: (response) => {
          if (response.data && response.data['ServerUCP'] && response.data['ServerUCP']['ucpId']) {
            currentUCPId = response.data['ServerUCP']['ucpId'];
          }
          else {
            currentUCPId = null;
          }

          if (currentUCPId == null) {

            clearInterval(timer)
            console.count("Error getting ucpId - " + JSON.stringify(currentUCPId))
            timer = setInterval(registerProviderConnnection, parseInt(process.env.FAILED_TIMEOUT))
          } else {

            const messageData: DataTypeFormat &
              (
                | { NotificationData: NotificationData }
                | { NotificationMicroserviceData: NotificationMicroserviceData }
                | { DatabaseNotificationData: DatabaseNotificationData }
              )
              = {
              NotificationMicroserviceData: {
                message: "this is my message notify"
              }
            }
            //emit to UCP server
            DPControllerInstance.emit(appname, DPControllerInstance.getMessageService().getNotificationMessage(currentUCPId, messageData, 'Notify')).subscribe({
              complete() {
                console.log('[TEST]Done EMIT.');
              }
            })
            const msg = DPControllerInstance.getMessageService().getSubscribeNotifMessage(currentUCPId, 'notification');

            //test to receive notification from emitter
            DPControllerInstance.send(appname, msg).subscribe({
              next(response_msg) {


                if (response_msg.header?.messageType === 'ResponseData') {
                  // console.log(response_msg.data['data']?.NotificationMicroserviceData)
                }
              }
              ,
              error(err) {
                const message = err.message || err;
                console.error('[TEST3]Something wrong occurred: ' + message);
              },
              complete() {
                console.log('[TEST3]Done observable 2.');
              }
            })
            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()
  }


  registerServiceProvider(DPControllerInstance: DomainProxyController) {

    if (!this.MessageService) {
      this.MessageService = new FisCreateMessageUtility('getLoginMessage');
    }
    const provider1_Id = "Provider1";
    const appname: string = "Provider Application";
    let currentUCPId: string = "";

    const msg = DPControllerInstance.getMessageService().getLoginMessage();

    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");

    QueryClientController.provider1.processNotification(newNotificationMessage1);

    const settings: ConnectionInterface = {
      IdName: appname,
      Description: "Testing login",
      Type: "Http",
      Target: "http://swopt.com:3011",
    }
    DPControllerInstance.setConnection(settings)

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

    let timer;

    const registerProviderConnnection = () => {

      DPControllerInstance.setIsConnectionStatusObserved(appname, true);
      DPControllerInstance.send(appname, msg).subscribe({
        next: (response) => {
          if (response.data && response.data['ServerUCP'] && response.data['ServerUCP']['ucpId']) {
            currentUCPId = response.data['ServerUCP']['ucpId'];
          }
          else {
            currentUCPId = null;
          }

          if (currentUCPId == null) {
            console.log(response)
            clearInterval(timer)
            console.count("Error getting ucpId - " + JSON.stringify(currentUCPId))
            timer = setInterval(registerProviderConnnection, parseInt(process.env.FAILED_TIMEOUT))
          } else {
            const msg = DPControllerInstance.getMessageService().getSubscribeNotifMessage(currentUCPId, 'serviceProvider');

            DPControllerInstance.send(appname, msg).subscribe({
              next(response_msg) {
                if (response_msg.data?.['ServiceProvidersData']?.providers.length > 0) {

                  response_msg.data?.['ServiceProvidersData']?.providers.map(x => {

                    QueryClientController.provider1.addGraphQLProvider(x.id, x)
                  })
                }

              }
              ,
              error(err) {
                const message = err.message || err;
                console.error('[TEST3]Something wrong occurred: ' + message);
              },
              complete() {
                console.log('[TEST3]Done observable 2.');
              }
            })
            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 {
  const messageId = new Uuid();
  return messageId.generateId().toString();
}