import { AuthConfig } from './../config';
import { inject, observable } from "aurelia-framework";
import { UserProfile } from '../model/common-model';
import { Datacontext } from '../data/data-context';
import { Config, PermissionSetting, RouteSettings } from "../config";
import { LoginMsg } from "./login-msg";

export abstract class AuthService {
  @observable
  public currentUser: UserProfile;
  authProfile: any;
  abstract navigateToSavedInstruction(): void;
  abstract getProfile(): Promise<void>;
  abstract isAuthenticated(): Promise<boolean>;
  abstract accessToken(): Promise<string>;
  abstract checkAccess(resource: string, action: string): boolean;
  abstract logout(): Promise<void>;
  abstract login(navInstruction: string): Promise<void>;
  abstract handleAuthentication(): Promise<void>;
  abstract addBearerTokenToHttpClient(): void;
  abstract onUserPermissionsChanged(): Promise<void>;
  abstract get usernameType(): 'email' | 'username';

  constructor(protected config: Config) { }

  checkRouteAccess(route: RouteSettings) {
    let actions = this.extractActions(route);
    if (route.resource && actions.length > 0) {
      return this.checkPermissonAccess(route);
    } else if (route.permissionSettings?.length > 0) {
      return route.permissionSettings.some(x => this.checkPermissonAccess(x));
    }
    return true;
  }

  checkPermissonAccess(permission: PermissionSetting) {
    let actions = this.extractActions(permission);
    if (permission.resource && actions.length > 0) {
      return actions.some(x => this.checkAccess(permission.resource, x));
    }
    return true;
  }

  extractActions(setting: RouteSettings): string[] {
    return setting.action
      ? (typeof setting.action == 'string' ? [setting.action] : setting.action)
      : [];
  }

  protected requestRequiresAuth(message: Request): boolean {
    let apiEndpoint = this.config.globalConfig.remoteServiceName;
    // Force trailing slash
    if (false == apiEndpoint.endsWith('/')) {
      apiEndpoint += '/';
    }

    let anonymousUrls = this.config.authConfig?.anonymousUrls?.map(url => {
      // Remove starting slash
      if (url.startsWith('/')) {
        url = url.substring(1);
      }
      // Return full URL
      return url.startsWith(apiEndpoint) ? url : apiEndpoint + url;
    }) ?? [];

    // Remove query params
    let url = message.url;
    let queryIndex = url.indexOf('?');
    if (queryIndex > 0) {
      url = url.substring(0, queryIndex);
    }

    return url.startsWith(apiEndpoint) && false == anonymousUrls.some(x => x == url);
  }
}

@inject(Datacontext)
export class FakeAuthService extends AuthService {
  constructor(config: Config, private dataContext: Datacontext) {
    super(config);
  }
  get usernameType(): "email" | "username" {
    return "email";
  }
  public addBearerTokenToHttpClient() {

  }
  navigateToSavedInstruction() {

  }

  async login(navInstruction: string) {
    throw new Error("Method not implemented.");
  }

  async logout() {
    throw new Error("Method not implemented.");
  }

  async handleAuthentication() {
    throw new Error("Method not implemented.");
  }

  async isAuthenticated() {
    return true;
  }

  async accessToken() {
    return "fake-token";
  }

  checkAccess(resource: string, action: string) {
    return true;
  }

  async getProfile() {
    this.currentUser = await this.dataContext.getCurrentUserProfile();
  }

  async onUserPermissionsChanged(): Promise<void> {

  }
}