import AppResponse from '@/response/app-response';
import {
  BadRequestException,
  HttpStatus,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import * as bcrypt from 'bcrypt';
import { Request } from 'express';
import {
  FilterOperator,
  FilterSuffix,
  paginate,
  PaginateQuery,
} from 'nestjs-paginate';
import { Column } from 'nestjs-paginate/lib/helper';
import { In, Not, Repository } from 'typeorm';
import Admin from '../entities/admin.entity';
import Permission from '../entities/permission.entity';

@Injectable()
export class AdminsService {
  constructor(
    @InjectRepository(Admin)
    readonly adminRepo: Repository<Admin>,
    @InjectRepository(Permission)
    readonly permissionRepo: Repository<Permission>,
  ) {}

  async index(query: PaginateQuery) {
    const filterableColumns: {
      [key in Column<Admin> | (string & {})]?:
        | (FilterOperator | FilterSuffix)[]
        | true;
    } = {
      id: true,
      username: [FilterOperator.ILIKE],
      fullname: [FilterOperator.ILIKE],
      email: [FilterOperator.ILIKE],
    };

    query.filter = AppResponse.processFilters(query.filter, filterableColumns);

    const data = await paginate(query, this.adminRepo, {
      sortableColumns: ['id', 'username', 'email', 'created_at', 'updated_at'],
      searchableColumns: ['id', 'username', 'email'],
      defaultLimit: 15,
      filterableColumns,
      defaultSortBy: [['id', 'ASC']],
      maxLimit: 100,
      relations: {
        permissions: true,
      },
    });

    return AppResponse.toPagination<Admin>(data, true, Admin);
  }

  async delete(id: Admin['id']) {
    const admin = await this.adminRepo.findOne({ where: { id } });

    if (!admin)
      throw new NotFoundException(
        AppResponse.toResponse(false, {
          message: 'Admin is not found',
          status_code: HttpStatus.NOT_FOUND,
        }),
      );

    if (admin.is_system_account) {
      throw new BadRequestException(
        AppResponse.toResponse(false, {
          message: "Can't delete this account",
          status_code: HttpStatus.BAD_REQUEST,
        }),
      );
    }

    await this.adminRepo.delete({ id: admin.id });

    return AppResponse.toResponse(true, { message: 'Delete success !' });
  }

  async deletes(ids: Admin['id'][], request: Request) {
    const curAdmin = request['admin'];

    let newIds = ids;

    if (curAdmin) {
      newIds = ids.filter((item) => item !== curAdmin?.id);
    }

    const result = await this.adminRepo.delete({
      id: In(ids),
      is_system_account: false,
    });

    if (!result.affected) {
      throw new BadRequestException(
        AppResponse.toResponse(false, {
          message: 'No items have been deleted yet.',
          status_code: HttpStatus.BAD_REQUEST,
        }),
      );
    }

    return AppResponse.toResponse(true, { message: 'Delete success !' });
  }

  async create({
    password,
    ...data
  }: Omit<
    Admin,
    | 'id'
    | 'created_at'
    | 'updated_at'
    | 'is_system_account'
    | 'permissions'
    | 'posts'
    | 'generateKeys'
  >) {
    const hashPassword = await bcrypt.hash(password, 10);

    const newAdmin = await this.adminRepo.save({
      ...data,
      password: hashPassword,
    });

    if (!newAdmin)
      throw new BadRequestException(
        AppResponse.toResponse(false, {
          message: "Can't create account",
          status_code: HttpStatus.BAD_REQUEST,
        }),
      );

    return AppResponse.toResponse(true, { message: 'Create success' });
  }

  async grantNewPassword(id: Admin['id'], password: string, request: Request) {
    const hashPassword = await bcrypt.hash(password, 10);

    const admin = request['admin'];

    if (admin && admin.id == id) {
      throw new BadRequestException(
        AppResponse.toResponse(false, {
          message:
            'This future not support for this account, Please use change password !',
          status_code: HttpStatus.BAD_REQUEST,
        }),
      );
    }

    const result = await this.adminRepo.update(id, {
      password: hashPassword,
    });

    if (!result)
      throw new BadRequestException(
        AppResponse.toResponse(false, {
          message: "Can't create account",
          status_code: HttpStatus.BAD_REQUEST,
        }),
      );

    return AppResponse.toResponse(true, { message: 'Grant success' });
  }

  async update(id: Admin['id'], { permissions, ...data }: Partial<Admin>) {
    try {
      const admin = await this.adminRepo.findOne({ where: { id } });

      if (!admin)
        throw new NotFoundException(
          AppResponse.toResponse(false, {
            message: 'Admin is not found',
            status_code: HttpStatus.NOT_FOUND,
          }),
        );

      if (data.email) {
        const isDuplicateAdmin = await this.adminRepo.findOne({
          where: { email: data.email, id: Not(id) },
        });

        if (isDuplicateAdmin) {
          throw new BadRequestException(
            AppResponse.toResponse(false, {
              message: 'Email is already exits!',
              status_code: HttpStatus.BAD_REQUEST,
            }),
          );
        }
      }

      if (permissions) {
        const permissionIds = permissions.map((p) => p.id);
        const permissionsData = await this.permissionRepo.findBy({
          id: In(permissionIds),
        });

        admin.permissions = permissionsData;
      }

      if (data.fullname) {
        admin.fullname = data.fullname;
      }

      await this.adminRepo.save(admin);

      return AppResponse.toResponse(true, { message: 'Update success !' });
    } catch (error) {
      throw new BadRequestException(
        AppResponse.toResponse(null, {
          message: error.message,
          status_code: HttpStatus.INTERNAL_SERVER_ERROR,
        }),
      );
    }
  }
}
