import { Directive } from '@angular/core';
import { inject } from '@angular/core';
import { AccountService } from 'app/core/auth/account.service';
import { Resources } from 'app/security/resources.model';
import { SecurityAction } from 'app/security/security-action.model';
import { ResilientService } from 'app/resilient/resilient-base/resilient-base.service';
import { IResilientEntity } from 'app/resilient/resilient-base/resilient-base.model';
import { SecurityPermission } from 'app/security/security-permission.model';
import { ISecurityGroupPermission } from 'app/security/security-group/security-group-permission.model';

/**
 * Abstract Resilient entity component that provides basic generic functions.
 * @param <IE> Entity domain interface (Ex. Period)
 * @param <NE> Entity domain new interface (Ex. NewPeriod)
 * @template S - The type of service this component depends on.
 */
@Directive() // Use Directive to mark it as an abstract class; not instantiated directly
export abstract class AbstractResilientEntityComponent<IE extends IResilientEntity, NE, S extends ResilientService<IE, NE>> {  
  private createPermissionCache: SecurityPermission | undefined;
  private readPermissionCache: SecurityPermission | undefined;
  private updatePermissionCache: SecurityPermission | undefined;
  private deletePermissionCache: SecurityPermission | undefined;

  private allowPermissions = [SecurityPermission.ALL, SecurityPermission.HIERARCHY]; 

  protected accountService = inject(AccountService);
  

  /**
   * This makes the enum accessible in the template
   * Use it, mainly, in html templates for security.
   * Example:
   *    <button *ngIf="hasCreatePermission(Resources.UNIT_TYPE)"></button>
   */
  Resources = Resources; 

  /**
   * The constructor for the abstract class.
   * @param service - A service instance of type S, used to fetch data.
   */
  constructor(protected service: S) {
    this.service = service;
  }
  
  /* 
   * Security Permission methods. 
   * Evaluates if the current authenticated user has PERMISSION to do the action 
   * In html template, if security permission fails, the secured element is hidden.
   * Usage (delete):
   *    <button *ngIf="hasDeletePermission(Resources.UNIT_TYPE)"></button>
   * 
   * Usage (save):
   *    <button *ngIf="(editForm.controls.id.value!=null && hasUpdatePermission(Resources.UNIT_TYPE))
                      ||
                      (editForm.controls.id.value==null && hasCreatePermission(Resources.UNIT_TYPE))"></button>
   */
  getCreatePermission(evalResource: Resources): SecurityPermission {
    if (this.createPermissionCache  === undefined) {
      this.createPermissionCache = this.accountService.getPermission(evalResource, SecurityAction.CREATE);
    }
    return this.createPermissionCache;
  }

  getReadPermission(evalResource: Resources): SecurityPermission {
    if (this.readPermissionCache  === undefined) {
      this.readPermissionCache = this.accountService.getPermission(evalResource, SecurityAction.READ);
    }
    return this.readPermissionCache;
  }

  getUpdatePermission(evalResource: Resources): SecurityPermission {
    if (this.updatePermissionCache  === undefined) {
      this.updatePermissionCache = this.accountService.getPermission(evalResource, SecurityAction.UPDATE);
    }
    return this.updatePermissionCache;
  }

  getDeletePermission(evalResource: Resources): SecurityPermission {
    if (this.deletePermissionCache  === undefined) {
      this.deletePermissionCache = this.accountService.getPermission(evalResource, SecurityAction.DELETE);
    }
    return this.deletePermissionCache;
  }
  
  hasCreatePermission(evalResource: Resources): boolean {
    return this.allowPermissions.includes(this.getCreatePermission(evalResource));
  }

  hasReadPermission(evalResource: Resources): boolean {
    return this.allowPermissions.includes(this.getReadPermission(evalResource));
  }

  hasUpdatePermission(evalResource: Resources): boolean {
    return this.allowPermissions.includes(this.getUpdatePermission(evalResource));
  }

  hasDeletePermission(evalResource: Resources): boolean {
    return this.allowPermissions.includes(this.getDeletePermission(evalResource));
  }

  /* 
   * Action Permission methods. 
   * Evaluates if the current authenticated user AND/OR 
   * the current Entity instance allows the action to be done. Example, the User hasPermission to DELETE, 
   * but the Entity instance is in a state that DOESN'T allow it.
   * In html template, if action permission fails, the element is showned but disabled.
   * Usage:
   *    <button 
   *      type="button" 
   *      [routerLink]="!canUpdate(inputDataUpload()) ? null : ['/input-data-upload', inputDataUpload()!.id, 'edit']" 
   *      [class.disabled]="!canUpdate(inputDataUpload())"
   *    > </button>
   *    
   * #1 :  [routerLink]="!canUpdate(inputDataUpload()) ? null : ['/input-data-upload', inputDataUpload()!.id, 'edit']" 
   *       Adds the link ONLY if the entity allows to be updated. Otherwise, only shows the value.
   * #2 :  [class.disabled]="!canUpdate(inputDataUpload())"
   *       Adds a css class if the entity dpesn't allows to be updated. This improves UX.
   */
  canCreate(): boolean {
    return true;
  }

  canRead(entity: IE | null): boolean {
    if (!entity) return false;
    return this.service.canRead(entity);
  }

  canUpdate(entity: IE | null): boolean {
    if (!entity) return false;
    return this.service.canUpdate(entity);
  }

  canDelete(entity: IE | null): boolean {
    if (!entity) return false;
    return this.service.canDelete(entity);
  }
}