import {
  AfterLoad,
  BaseEntity,
  BeforeInsert,
  BeforeUpdate,
  Column,
  Entity,
  Generated,
  Index,
  JoinColumn,
  ManyToOne,
  OneToMany,
  PrimaryGeneratedColumn,
} from 'typeorm';
import { AddressEntity } from '../../address/entities/address.entity';
import * as bcrypt from 'bcryptjs';
import * as Cryptr from 'cryptr';
import { OrderEntity } from '../../order/entities/order.entity';
import { ServiceReminderEntity } from '../../service/entities/service-reminder.entity';
import { CarEntity } from '../../car/entities/car.entity';
import { PollEntity } from '../../poll/entities/poll.entity';
import { PointEntity } from '../../point/entities/point.entity';
import { TicketEntity } from '../../ticket/entities/ticket.entity';
import { ProductNotifQuantityEntity } from 'src/modules/product/entities/product-notif-quantity.entity';
import { NewsEntity } from 'src/modules/news/entities/news.entity';
import { NewscommentEntity } from 'src/modules/news/entities/news-comment.entity';
import { RoleEntity } from '../../role/entities/role.entity';
import { BooleanTransformer } from 'src/utils/transformers/BooleanTransformer';
import { LogEntity } from '../../log/entities/log.entity';
import { WithdrawEntity } from 'src/modules/withdraw/entities/withdraw.entity';
import { CustomerEntity } from '../../customer/entities/customer.entity';
import { BonusCreditsEntity } from '../../bonus-credit/entities/bonus-credit.entity';
import { ProductEntity } from '../../product/entities/product.entity';
import { DiscountEntity } from '../../discount/entities/discount.entity';
import { NegativeScoreEntity } from '../../negative-score/entities/negative-score.entity';
import { DrivingOffenseEntity } from '../../drivinig-offense/entities/driving-offense.entity';
import { PaymentEntity } from '../../payment/entities/payment.entity';
import { FreewayTollEntity } from '../../freeway-toll/entities/freeway-toll.entity';
import { InsuranceThirdPartyInquiryEntity } from '../../insurance/entities/insurance-third-party-inquiry.entity';

export enum RoleIds {
  Admin = 2,
  ServiceMan = 3,
  User = 1,
}

export const Roles = {
  1: 'user',
  2: 'admin',
  3: 'service_man',
};

export enum Gender {
  male = 'male',
  female = 'female',
}

const secretPassword = 'SLW7INJ45L';

@Index('users_mobile', ['mobile'], { unique: true })
@Index('roleId', ['roleId'], {})
@Index('groupId', ['groupId'], {})
@Entity('users')
export class UserEntity extends BaseEntity {
  @Generated('uuid')
  @PrimaryGeneratedColumn('uuid')
  @Column('char', { primary: true, name: 'id', length: 36 })
  id: string;

  @Column('varchar', { name: 'email', nullable: true, length: 255 })
  email: string | null;

  @Column('varchar', { name: 'password', nullable: true, length: 50 })
  password: string | null;

  @Column('varchar', { name: 'name', nullable: true, length: 50 })
  name: string | null;

  @Column('varchar', { name: 'surName', nullable: true, length: 50 })
  surName: string | null;

  @Column('varchar', { name: 'nationalCode', nullable: true, length: 45 })
  nationalCode: string | null;

  @Column('enum', {
    name: 'gender',
    enum: Gender,
    nullable: true,
  })
  gender: Gender | null;

  @Column('date', { name: 'birthday', nullable: true })
  birthday: string | null;

  @Column('varchar', { name: 'phone', nullable: true, length: 50 })
  phone: string | null;

  @Column('varchar', {
    name: 'mobile',
    nullable: true,
    unique: true,
    length: 50,
  })
  mobile: string | null;

  @Column('varchar', { name: 'address', nullable: true, length: 50 })
  address: string | null;

  @Column('varchar', { name: 'licenseNumber', nullable: true, length: 128 })
  licenseNumber: string | null;

  @Column('int', { name: 'virtualWallet', default: () => "'0'" })
  virtualWallet: number;

  @Column('int', { name: 'realWallet', default: () => "'0'" })
  realWallet: number;

  @Column('varchar', { name: 'bankName', nullable: true, length: 45 })
  bankName: string | null;

  @Column('varchar', { name: 'accountNumber', nullable: true, length: 45 })
  accountNumber: string | null;

  @Column('varchar', { name: 'cardNumber', nullable: true, length: 45 })
  cardNumber: string | null;

  @Column('varchar', { name: 'sheba', nullable: true, length: 128 })
  sheba: string | null;

  @Column('tinyint', { name: 'isServiceman', nullable: true, width: 1 })
  isServiceman: boolean | null;

  @Column('tinyint', { name: 'isActive', nullable: true, width: 1 })
  isActive: boolean | null;

  @Column('tinyint', {
    name: 'isAdmin',
    nullable: true,
    width: 1,
    default: () => "'0'",
  })
  isAdmin: boolean | null;

  @Column('int', { name: 'roleId' })
  roleId: RoleIds;

  @Column('int', { name: 'groupId', nullable: true })
  groupId: number | null;

  @Column('tinyint', {
    name: 'isActiveGroup',
    nullable: true,
    width: 1,
    default: () => "'0'",
  })
  isActiveGroup: boolean | null;

  @Column('varchar', { name: 'codeRegister', nullable: true, length: 5 })
  codeRegister: string | null;

  @Column('int', { name: 'countSMS', default: () => "'0'" })
  countSms: number;

  @Column('int', { name: 'isBlock', default: () => "'0'" })
  isBlock: number;

  @Column('varchar', { name: 'userKey', nullable: true, length: 8 })
  userKey: string | null;

  @Column('varchar', { name: 'moaref', nullable: true, length: 8 })
  moaref: string | null;

  @Column('varchar', { name: 'avatar', nullable: true, length: 255 })
  avatar: string | null;

  @Column('tinyint', {
    name: 'deleted',
    nullable: true,
    width: 1,
    default: () => "'0'",
    transformer: new BooleanTransformer(),
  })
  deleted: boolean | null;

  @Column('int', { name: 'customerId', nullable: true })
  customerId: number | null;

  @Column('tinyint', {
    name: 'isImported',
    width: 1,
    default: () => "'0'",
    transformer: new BooleanTransformer(),
  })
  isImported: boolean;

  @Column('datetime', { name: 'lastActivityAt', nullable: true })
  lastActivityAt: Date | null;

  @Column('datetime', { name: 'lastSendAdvertisementAt', nullable: true })
  lastSendAdvertisementAt: Date | null;

  @Column('datetime', { name: 'createdAt' })
  createdAt: Date;

  @Column('datetime', { name: 'updatedAt' })
  updatedAt: Date;

  /**
   * relations
   */
  @ManyToOne(() => CustomerEntity, (customers) => customers.users, {
    onDelete: 'NO ACTION',
    onUpdate: 'NO ACTION',
  })
  @JoinColumn([{ name: 'customerId', referencedColumnName: 'id' }])
  customer: CustomerEntity;

  @OneToMany(() => AddressEntity, (address) => address.user)
  addresses: AddressEntity[];

  @OneToMany(() => CarEntity, (cars) => cars.user)
  cars: CarEntity[];

  @OneToMany(() => OrderEntity, (orders) => orders.user)
  orders: OrderEntity[];

  @OneToMany(() => OrderEntity, (orders) => orders.serviceMan)
  serviceMans: OrderEntity[];

  @OneToMany(
    () => ServiceReminderEntity,
    (serviceReminders) => serviceReminders.user,
  )
  serviceReminders: ServiceReminderEntity[];

  @OneToMany(() => PollEntity, (polls) => polls.user)
  polls: PollEntity[];

  @OneToMany(() => PointEntity, (points) => points.user)
  points: PointEntity[];

  // @OneToMany(() => Scores, (scores) => scores.user)
  // scores: Scores[];

  // @OneToMany(
  //   () => SurveiesUsersMappings,
  //   (surveiesUsersMappings) => surveiesUsersMappings.user,
  // )
  // surveiesUsersMappings: SurveiesUsersMappings[];

  @OneToMany(() => TicketEntity, (tickets) => tickets.user)
  tickets: TicketEntity[];

  @ManyToOne(() => RoleEntity, (roles) => roles.users, {
    onDelete: 'NO ACTION',
    onUpdate: 'CASCADE',
  })
  @JoinColumn([{ name: 'roleId', referencedColumnName: 'id' }])
  role: RoleEntity;

  @OneToMany(
    () => ProductNotifQuantityEntity,
    (productNotifQuantities) => productNotifQuantities.user,
  )
  productNotifQuantities: ProductNotifQuantityEntity[];

  @OneToMany(() => NewsEntity, (news) => news.user)
  news: NewsEntity[];

  @OneToMany(() => NewscommentEntity, (newscomments) => newscomments.user)
  newscomments: NewscommentEntity[];

  @OneToMany(() => WithdrawEntity, (withdraw) => withdraw.user)
  withdraws: WithdrawEntity[];

  @OneToMany(() => OrderEntity, (orders) => orders.seller)
  ordersSeller: OrderEntity[];

  @OneToMany(() => LogEntity, (logs) => logs.operatorUser)
  operatorlogs: LogEntity[];

  @OneToMany(() => LogEntity, (logs) => logs.affectedUser)
  affectedLogs: LogEntity[];

  @OneToMany(() => BonusCreditsEntity, (bonusCredits) => bonusCredits.user)
  bonusCredits: BonusCreditsEntity[];

  @OneToMany(() => ProductEntity, (products) => products.creatorUser)
  products: ProductEntity[];

  @OneToMany(() => DiscountEntity, (discounts) => discounts.user)
  discounts: DiscountEntity[];

  @OneToMany(() => NegativeScoreEntity, (negativeScore) => negativeScore.user)
  negativeScores: NegativeScoreEntity[];

  @OneToMany(
    () => DrivingOffenseEntity,
    (drivingOffense) => drivingOffense.user,
  )
  drivingOffenses: DrivingOffenseEntity[];

  @OneToMany(() => PaymentEntity, (payments) => payments.user)
  payments: PaymentEntity[];

  @OneToMany(() => FreewayTollEntity, (freewayTolls) => freewayTolls.user)
  freewayTolls: FreewayTollEntity[];

  @OneToMany(
    () => InsuranceThirdPartyInquiryEntity,
    (insuranceThirdPartyInquiries) => insuranceThirdPartyInquiries.user,
  )
  insuranceThirdPartyInquiries: InsuranceThirdPartyInquiryEntity[];

  // @ManyToOne(() => Usergroups, (usergroups) => usergroups.users, {
  //   onDelete: 'NO ACTION',
  //   onUpdate: 'CASCADE',
  // })
  // @JoinColumn([{ name: 'groupId', referencedColumnName: 'id' }])
  // group: Usergroups;

  /**
   * Helpers
   */

  /**
   * Entity Listeners
   * https://typeorm.io/listeners-and-subscribers
   */
  public previousPassword: string;

  @AfterLoad()
  public loadPreviousPassword() {
    this.previousPassword = this.password;
  }

  @BeforeInsert()
  @BeforeUpdate()
  async setPassword() {
    if (this.previousPassword !== this.password && this.password) {
      const cryptr = new Cryptr(secretPassword);
      this.password = cryptr.encrypt(this.password);
    }
  }

  async isCorrectSaltPassword(plainPassword: string): Promise<boolean> {
    return await bcrypt.compare(plainPassword, this.password);
  }

  isCorrectEncryptPassword(plainPassword: string): boolean {
    if (!this.password || !plainPassword) return false;

    const cryptr = new Cryptr(secretPassword);
    const decrypted = cryptr.decrypt(this.password);
    const match = plainPassword === decrypted;
    return match;
  }
}
