import { Injectable } from "@angular/core";
import { Request } from "../models/request";
import { TranslatorService } from "../translator/translator.service";
import { ConnectorService } from "../connector/connector.service";
import { Observable, throwError } from "rxjs";
import { CheckPatientRegisterationRequest } from "./models/check-patient-registeration.request";
import { LoginRequest } from "./models/login.request";
import { Response } from "../models/response";
import { AuthenticatedUser } from "./models/authenticated-user";
import { CommonService } from "../common/common.service";
import { CheckUserAuthenticationRequest } from "./models/check-user-auth.request";
import { CheckActivationCodeRequest } from "./models/check-activation-code.request";
import { CheckUserAuthenticationResponse } from "./models/check-user-auth.response";
import { CheckActivationCodeResponse } from "./models/check-activation-code.response";
import { NativeStorage } from "@ionic-native/native-storage/ngx";
import { CheckRegisterationCodeRequest } from "./models/check-registeration-code.request";
import { RegisterInformationRequest } from "./models/register-information.request";
import { ForgotFileIDResponse } from "./models/forgot-File-ID.response";
import { EmailRequest } from "../models/email-request";
import { EmailInput } from "../../ui/email/models/email-input";
import { UserLocalNotificationService } from "../user-local-notification/user-local-notification.service";
import { GetLoginInfoRequest } from "./models/get-login-info.request";
import { GetLoginInfoResponse } from "./models/get-login-info.response";
import { analyzeAndValidateNgModules } from "@angular/compiler";
import { Events } from "@ionic/angular";
import { InternationalMobileComponent } from "../../ui/mobile-number/international-mobile/international-mobile.component";
import { SMSCheckRequest } from "./models/smscheck.request";
import { SMSCheckResponse } from "./models/smscheck.response";
import { ForgetPassword } from 'src/app/authentication/models/forget.password';

@Injectable({
  providedIn: "root"
})
export class AuthenticationService {


    public static MOBILE_USER = 102;

    /* login methods */
    public static loginURL = 'Services/Authentication.svc/REST/CheckPatientAuthentication';
    public static checkUserAuthURL = 'Services/Authentication.svc/REST/CheckPatientAuthentication';
    
    public static login = 'Services/ERP.svc/REST/MemberLogin';

    public static activationCodeURL = 'Services/Authentication.svc/REST/CheckActivationCode';
    public static getLoginInfoURL = 'Services/Authentication.svc/REST/GetMobileLoginInfo';

    public static smsCheck='Services/ERP.svc/REST/CheckActivationCode';
    public static smsCheckForget='Services/ERP.svc/REST/CheckPublicActivationCode';
    public static smsSendCode='Services/ERP.svc/REST/SendPublicActivationCode';

    /* register methods */

    public static checkPatientForRegisterationURL = 'Services/Authentication.svc/REST/CheckPatientForRegisteration';
    public static sendSmsForRegisterURL = 'Services/Authentication.svc/REST/SendSMSForPatientRegisteration';
    public static registerTempUserURL = 'Services/Authentication.svc/REST/RegisterTempPatientWithoutSMS';

    public static sendSMSForgotFileNoURL = 'Services/Authentication.svc/REST/SendPatientIDSMSByMobileNumber';
    public static forgotFileIDURL = 'Services/Authentication.svc/REST/CheckActivationCodeForSendFileNo';
    public static user: AuthenticatedUser;

    /*user checking methods */
   public static userChecking = 'Services/ERP.svc/REST/Get_BasicUserInformation';
   public static changePasswordForget ='Services/ERP.svc/REST/ChangePassword_Forget';

    public static LOGIN_EVENT = 'user-login-event';
    public static FAMILY_LOGIN_EVENT = 'family-login-event';
    public static AUTHENTICATED_USER_KEY = 'save-authenticated-user';


    

    // private static user: AuthenticatedUser;
    constructor(
        public con: ConnectorService,
        public cs: CommonService,
        public ts: TranslatorService,
        public nativeStorage: NativeStorage,
        public localNotifications: UserLocalNotificationService,
        private events: Events
    ) { }

    public authenticateRequest(request: Request, automaticLogin = true): Request {

        this.setPublicFields(request);
        const user = this.getAuthenticatedUser();
        if (user) {
            request.P_SESSION_ID = user.P_SESSION_ID;
            request.MobileNumber = user.EMPLOYEE_MOBILE_NUMBER;
            request.LogInTokenID = user.LogInTokenID;
            request.TokenID = user.TokenID;
            request.P_USER_NAME = user.EMPLOYEE_NUMBER;
            request.UserName = user.EMPLOYEE_NUMBER;
            if (AuthenticationService.requireRelogin) {
                this.sessionTimeOutDialog();
            }
        } else {
            this.cs.userNeedToReLogin();
        }
        /*
        else {
            if (automaticLogin) {
                this.cs.openUserLogin();
            }
        }
        */
    return request;
  }

  public setPublicFields(request: Request): Request {
    request.VersionID = 1;
    request.Channel = 31;
    request.LanguageID = TranslatorService.getCurrentLanguageCode();
    //request.IPAdress = '10.10.10.10';
    //request.SessionID = "any thing"; // ??? required for not authorized login funny
    request.MobileType = "";
    //request.isDentalAllowedBackend = false;
    return request;
  }
  public authenticateAndSetPersonalInformation(
    request: EmailRequest,
    examinationInfo?: any
  ): EmailRequest {
    // if not authenticated will be redirected to login
    this.authenticateRequest(request);
    const user = this.getAuthenticatedUser();
    if (user) {
      // request.To = user.Email;
      // request.DateofBirth = user.DateofBirth;
      // request.PatientIditificationNum = user.PatientIdentificationNo;
      // request.PatientMobileNumber = user.MobileNo;
      // request.PatientName = user.PatientName;
      // request.PatientOutSA = user.PatientOutSA ? 1 : 0;
      // request.PatientTypeID = user.PatientTypeID;

      if (examinationInfo) {
        if (examinationInfo.SetupID) {
          request.SetupID = examinationInfo.SetupID;
        }
        if (examinationInfo.ProjectName) {
          request.ProjectName = examinationInfo.ProjectName;
        }
        if (examinationInfo.ClinicName) {
          request.ClinicName = examinationInfo.ClinicDescription;
        }
        if (examinationInfo.DoctorName) {
          request.DoctorName = examinationInfo.DoctorName;
        }
        if (examinationInfo.ProjectID) {
          request.ProjectID = examinationInfo.ProjectID;
        }
        if (examinationInfo.InvoiceNo) {
          request.InvoiceNo = examinationInfo.InvoiceNo;
        }
        if (examinationInfo.OrderDate) {
          request.OrderDate = this.cs.getDateISO(
            this.cs.evaluteDateAsObject(examinationInfo.OrderDate)
          );
        }
      }
    }
    return request;
  }


  public getAuthenticatedRequest(login = true): Request {
    const request = new Request();
    this.authenticateRequest(request, login);
    return request;
  }



    public getPublicRequest(): Request {
        const request = new Request();
        this.setPublicFields(request);
        return request;
    }


    public login(request: LoginRequest, onError: any, errorLabel: string): Observable<Response> {
        this.setPublicFields(request);
        return this.con.post(AuthenticationService.login, request, onError, errorLabel);

    }

    public isAuthenticated(): boolean {
        return AuthenticationService.user != null;
    }

 

    /**
     * this fucntion load from user information if he logged in before
     * and save user into memory and local storage
     * disable notifications for previous user if new user logged in with different user
     *
     *
     * info:
     * 1- user stored in local storage without token
     * @param result  check activation code result
     */
    public setAuthenticatedUser(
        result: CheckActivationCodeResponse
      ): Observable<boolean> {
        return Observable.create(observer => {
          this.loadAuthenticatedUser().subscribe(
            (loadedUser: AuthenticatedUser) => {
              AuthenticationService.requireRelogin = false;
              this.startIdleMonitoring();
              const user = this.updateAuthenticatedUser(result, loadedUser);
              /* we store in hd without token but with token in memory*/
              this.saveUserInStorage(user).subscribe((success: boolean) => {
                AuthenticationService.user = user;
    
                this.publishUserChangeEvent();
                observer.next(true);
                observer.complete();
              });
            }
          );
        });
      }
    
         
  public resetAuthenticatedUser(user: AuthenticatedUser) {
    AuthenticationService.user = user;
    AuthenticationService.requireRelogin = false;
    this.startIdleMonitoring();
    this.publishUserChangeEvent();
  }

  public setAuthenticatedMemberFamilyUser(result: CheckActivationCodeResponse) {
    const authUser = new AuthenticatedUser();
    AuthenticationService.requireRelogin = false;
    this.startIdleMonitoring();
    const user = this.updateAuthenticatedUser(result, authUser);

    // user["hello"]= "ok";
    /* we store in hd without token but with token in memory*/
    AuthenticationService.user = user;
    this.publishFamilyMemeberUserChangeEvent();
  }

  public updateLoggedInUser(newUser: AuthenticatedUser): Observable<boolean> {
    return Observable.create(observer => {
      /* we store in hd without token but with token in memory*/
      this.saveUserInStorage(newUser).subscribe((success: boolean) => {
        AuthenticationService.requireRelogin = false;
        AuthenticationService.user = newUser;
        this.publishUserChangeEvent();
        observer.next(success);
        observer.complete();
      });
    });
  }
  public setDeviceToken(token: string) {
    const user = this.getAuthenticatedUser();
    if (user) {
      // user.deviceToken = token;
      this.updateLoggedInUser(user).subscribe((success: boolean) => {
        // console.log('token stored:' + token);
      });
    }
  }

  public registerTempUser(
    request: RegisterInformationRequest,
    onError: any,
    errorLabel: string
  ): Observable<CheckActivationCodeResponse> {
    this.setPublicFields(request);
    request.TokenID = "";
    return this.con.post(
      AuthenticationService.registerTempUserURL,
      request,
      onError,
      errorLabel
    );
  }

  public getLoginInfo(
    request: GetLoginInfoRequest,
    onError: any,
    errorLabel: string
  ): Observable<GetLoginInfoResponse> {
    this.setPublicFields(request);
    return this.con.post(
      AuthenticationService.getLoginInfoURL,
      request,
      onError,
      errorLabel
    );
  }

  /**
   *update new authenticated user from previously stored and loaded user
   */
  private updateAuthenticatedUser(
    result: CheckActivationCodeResponse,
    loadedUser: AuthenticatedUser
  ): AuthenticatedUser {
    const user = result.MemberInformationList[0];
    user.P_SESSION_ID = result.P_SESSION_ID;
    user.MobileNumber = result.EMPLOYEE_MOBILE_NUMBER;
    user.TokenID = result.TokenID;
    user.CompanyImageDescription=result.CompanyImageDescription;
    user.CompanyImageURL=result.CompanyImageURL;
    user.LogInTokenID = this.cs.sharedService.getSharedData(
      "logindata"
    ).LogInTokenID;
    return user;
  }

  public isRealEmail(email: string): boolean {
    return EmailInput.isValidEmail(email);
  }

  public isAuthenticatedUserHasRealEmail(): boolean {
    const user = this.getAuthenticatedUser();
    return user ? this.isRealEmail(user.EMPLOYEE_EMAIL_ADDRESS) : false;
  }

  /**
   * we store in localstorage without token but we keep it in the static member
   */
  private saveUserInStorage(user: AuthenticatedUser): Observable<boolean> {
    return Observable.create(observer => {
      if (user) {
        // const TokenID = user.TokenID;
        // user.TokenID = null;
        this.nativeStorage
          .setItem(AuthenticationService.AUTHENTICATED_USER_KEY, user)
          .then(
            () => {
              //user.TokenID = TokenID;
              observer.next(true);
              observer.complete();
            },
            error => {
              // user.TokenID = TokenID;

              observer.next(false);
              observer.complete();
            }
          );
      } else {
        observer.next(false);
        observer.complete();
      }
    });
  }
  /**
   * signout
   *clear user session and storage information
   */
  public clearUser(): Observable<boolean> {
    this.clearUserSession();
    return Observable.create(observer => {
      this.publishUserChangeEvent();
      this.nativeStorage
        .remove(AuthenticationService.AUTHENTICATED_USER_KEY)
        .then(
          () => {
            observer.next(true);
            observer.complete();
          },
          () => {
            observer.next(false);
            observer.complete();
          }
        );
    });
  }

  private publishUserChangeEvent() {
    this.events.publish(
      AuthenticationService.LOGIN_EVENT,
      this.getAuthenticatedUser(),
      Date.now()
    );
  }

  private publishFamilyMemeberUserChangeEvent() {
    this.events.publish(
      AuthenticationService.FAMILY_LOGIN_EVENT,
      this.getAuthenticatedUser(),
      Date.now()
    );
  }

  public clearUserSession() {
    AuthenticationService.user = null;
  }

  public loadAuthenticatedUser(): Observable<AuthenticatedUser> {
    return Observable.create(observer => {
      this.nativeStorage
        .getItem(AuthenticationService.AUTHENTICATED_USER_KEY)
        .then(
          user => {
            observer.next(user);
            observer.complete();
          },
          error => {
            // this.getAuthenticatedUser()
            //commented for now will uncomment when we are going to deploy on the device;
            //observer.next(null);
            observer.next(AuthenticationService.user);
            observer.complete();
          }
        );
    });
  }
  // TODO you should read from local storage and update static member
  public getAuthenticatedUser(): AuthenticatedUser {
    /*
        if (AuthenticationService.requireRelogin) {
            this.sessionTimeOutDialog();
            return new AuthenticatedUser();
        } else {
            return AuthenticationService.user;
        }
        */
        return AuthenticationService.user;
    }


    public checkUserAuthentication(request: CheckUserAuthenticationRequest, onError: any, errorLabel: string)
        : Observable<CheckUserAuthenticationResponse> {
        this.setPublicFields(request);
        return this.con.post(AuthenticationService.userChecking, request, onError, errorLabel);
    }

    public checkActivationCode(request: CheckActivationCodeRequest, onError: any, errorLabel: string)
        : Observable<CheckActivationCodeResponse> {
        this.setPublicFields(request);
        return this.con.post(AuthenticationService.activationCodeURL, request, onError, errorLabel);
    }

    public checkSMS(request: SMSCheckRequest, onError: any, errorLabel: string)
        : Observable<SMSCheckResponse> {
        this.setPublicFields(request);
        return this.con.post(AuthenticationService.smsCheck, request, onError, errorLabel);
    }

    public checkForgetSMS(request: SMSCheckRequest, onError: any, errorLabel: string)
        : Observable<SMSCheckResponse> {
        this.setPublicFields(request);
        return this.con.post(AuthenticationService.smsCheckForget, request, onError, errorLabel);
    }

    public sendPublicSMS(request: SMSCheckRequest, onError: any, errorLabel: string)
        : Observable<CheckUserAuthenticationResponse> {
        this.setPublicFields(request);
        return this.con.post(AuthenticationService.smsSendCode, request, onError, errorLabel);
    }

    /*
    client side:
    id no , mobile no , zip code
    */

  /*
    client side:
       id no , mobile no , zip code
       */
    public sendSmsForPatientRegisteration(request: CheckPatientRegisterationRequest, onError: any, errorLabel: string)
        : Observable<CheckUserAuthenticationResponse> {
        this.setPublicFields(request);
        request.TokenID = '';
        request.isRegister = false;
        return this.con.post(AuthenticationService.sendSmsForRegisterURL, request, onError, errorLabel);
    }

    public checkRegisterationActivationCode(request: CheckRegisterationCodeRequest, onError: any, errorLabel: string)
        : Observable<Response> {
        this.setPublicFields(request);
        return this.con.post(AuthenticationService.activationCodeURL, request, onError, errorLabel);
    }

    public sendForgetPassword(request: ForgetPassword, onError: any, errorLabel: string)
        : Observable<CheckUserAuthenticationResponse> {
        this.setPublicFields(request);
        return this.con.post(AuthenticationService.smsSendCode, request, onError, errorLabel);
    }

    public submitForgetPassword(request: ForgetPassword, onError: any, errorLabel: string)
        : Observable<CheckUserAuthenticationResponse> {
        this.setPublicFields(request);
        return this.con.post(AuthenticationService.changePasswordForget, request, onError, errorLabel);
    }

    
    public sendSMSForForgotFileNumber(request: CheckUserAuthenticationRequest, onError: any, errorLabel: string)
        : Observable<CheckUserAuthenticationResponse> {
        this.setPublicFields(request);
        request.TokenID = '';
        //request.SearchType = 2;
        //request.isRegister = false;
        return this.con.post(AuthenticationService.sendSMSForgotFileNoURL, request, onError, errorLabel);
    }

    public forgotFileIdActivation(request: CheckActivationCodeRequest, onError: any, errorLabel: string)
        : Observable<ForgotFileIDResponse> {
        this.setPublicFields(request);
        request.TokenID = '';
        request.SearchType = 2;
        request.isRegister = false;
        return this.con.post(AuthenticationService.forgotFileIDURL, request, onError, errorLabel);
    }


    public isSAUDIIDValid(id: string): boolean {
        if (!id) {
          return false;
        }
        try {
          id = id.toString();
          id = id.trim();
          const returnValue = Number(id);
          let sum = 0;
          if (returnValue > 0) {
            const type = Number(id.charAt(0));
    
            if (id.length !== 10) {
              return false;
            }
            if (type !== 2 && type !== 1) {
              return false;
            }
    
            for (let i = 0; i < 10; i++) {
              if (i % 2 === 0) {
                const a = id.charAt(i);
                const x = Number(a) * 2;
                let b = x.toString();
                if (b.length === 1) {
                  b = "0" + b;
                }
                sum += Number(b.charAt(0)) + Number(b.charAt(1));
              } else {
                sum += Number(id.charAt(i));
              }
            }
            return sum % 10 === 0;
          }
        } catch (err) {}
        return false;
      }
    
  public checkUserHasEmailDialog(): Observable<void> {
    return Observable.create(observer => {
      if (this.isAuthenticatedUserHasRealEmail()) {
        const message = this.ts
          .trPK("general", "send-email")
          .replace("[0]", this.getAuthenticatedUser().EmailAddress);
        this.cs.presentConfirmDialog(message, () => {
          observer.next();
          observer.complete();
        });
      } else {
        this.cs.presentConfirmDialog(
          this.ts.trPK("login", "enter-email"),
          () => {
            //this.cs.openPatientProfile();
            observer.complete();
          }
        );
      }
    });
  }

  /*
    user session timeout after idle for 20 Minute
    */
  private static requireRelogin = false;
  public static monitorInterval_M = 20 * 60 * 1000;
  // public static monitorInterval_M = 15 * 1000;
  private static timerID;

  private stopIdleTimer() {
    if (AuthenticationService.timerID) {
      clearTimeout(AuthenticationService.timerID);
    }
  }

  private sessionTimeOutDialog() {
    this.cs.presentConfirmDialog(
      this.ts.trPK("general", "idle-relogin"),
      () => {
        // this.cs.openUserLogin();
      }
    );
  }

  /*
    reset and start idling interval
    */
  private detectIdle() {
    this.stopIdleTimer();
    AuthenticationService.timerID = setTimeout(() => {
      if (AuthenticationService.user) {
        AuthenticationService.requireRelogin = true;
        this.clearUserSession();
        this.publishUserChangeEvent();
        this.sessionTimeOutDialog();
        this.cs.openHome();
      }
    }, AuthenticationService.monitorInterval_M);
  }

  private registerDocumentEvents(events: string[], handler: any) {
    for (const event of events) {
      document.addEventListener(event, handler);
    }
  }

  private static eventsRegistered = false;
  public startIdleMonitoring() {
    if (!AuthenticationService.eventsRegistered) {
      AuthenticationService.eventsRegistered = true;
      // every time we have and event
      this.registerDocumentEvents(
        ["mousedown", "touchstart", "click", "keyup"],
        () => {
          // if user still logged in and session not expired
          if (
            AuthenticationService.user &&
            !AuthenticationService.requireRelogin
          ) {
            // reset and start idling timer
            this.detectIdle();
          }
        }
      );
    }
  }
}
