import { Password } from "@/contexts/common/domain";
import {
  AggregateRoot,
  Email,
  IDomainError,
  Language,
  Name,
  Result,
  UniqueID,
  handleDomainError,
} from "@shared/contexts";
import { UserHasName } from "./User.specifications";
import { UserRole } from "./UserRole";

export interface IUserProps {
  name: Name;
  email: Email;
  password: Password;
  roles: UserRole[];
  language: Language;
}

//type ISecuredUserProps = ;

export interface IUser {
  id: UniqueID;
  name: Name;
  email: Email;
  password: Password;
  language: Language;
  isUser: boolean;
  isAdmin: boolean;
}

export class User extends AggregateRoot<IUserProps> implements IUser {
  static readonly ERROR_USER_WITHOUT_NAME = "ERROR_USER_WITHOUT_NAME";

  public static create(props: IUserProps, id?: UniqueID): Result<User, IDomainError> {
    const user = new User(props, id);

    // Reglas de negocio / validaciones
    const isValidUser = new UserHasName().isSatisfiedBy(user);

    if (!isValidUser) {
      return Result.fail(handleDomainError(User.ERROR_USER_WITHOUT_NAME));
    }

    return Result.ok<User>(user);
  }

  public static async hashPassword(password): Promise<string> {
    return Password.hashPassword(password);
  }

  private roles: UserRole[];

  constructor(props: IUserProps, id?: UniqueID) {
    const { roles } = props;
    super(props, id);
    this.roles = roles;
  }

  get name(): Name {
    return this.props.name;
  }

  get email(): Email {
    return this.props.email;
  }

  get password(): Password {
    return this.props.password;
  }

  get language(): Language {
    return this.props.language;
  }

  get isUser(): boolean {
    return this.hasRole(UserRole.ROLE_USER);
  }

  get isAdmin(): boolean {
    return this.hasRole(UserRole.ROLE_ADMIN);
  }

  public hasRole(role: UserRole): boolean {
    return this.roles.some((r) => r.equals(role));
  }

  public getRoles(): UserRole[] {
    return this.roles;
  }

  public addRole(role: UserRole): void {
    if (!this.roles.some((r) => r.equals(role))) {
      this.roles.push(role);
    }
  }

  public removeRole(role: UserRole): void {
    this.roles = this.roles.filter((r) => !r.equals(role));
  }
}
