import { jwtDecode } from 'jwt-decode';
import express from 'express';
import axios from 'axios';

import { BorrowerCompliance } from '../models/BorrowerCompliance.model';
import { UserMobileAccess } from '../models/UserMobileAccess.model';
import { UserLog } from '../models/UserLog.model';
import { IUserRole, IUser, IUserAccess } from '../interfaces/auth-user.interface';

interface IAuthConfig {
  baseUrl: string;
}

export class UsersService {
  private readonly config: IAuthConfig;

  constructor(config: IAuthConfig) {
    this.config = config;
  }

  async getUserAccessByRequest(req: express.Request): Promise<IUserAccess | null> {
    const authorization = req.get('Authorization');
    if (!authorization) {
      return null;
    }
    const { sub }: { sub: string } = jwtDecode(authorization);
    const userAccess = await this.getUserAccess(sub);
    if (!userAccess) {
      return null;
    }
    return userAccess;
  }

  async getUserById(userId: string) {
    const { data } = await axios.get<IUser>(`${this.config.baseUrl}/users/by-id/${userId}`);
    return data ?? null;
  }

  async getUserByUsername(username: string) {
    const { data } = await axios.get<IUser>(`${this.config.baseUrl}/users/by-username/${username}`);
    return data ?? null;
  }

  async getUserByToken(req: express.Request) {
    const authorization = req.get('Authorization');
    if (!authorization) {
      return null;
    }
    const { sub }: { sub: string } = jwtDecode(authorization);
    return this.getUserById(sub);
  }

  async getUserIdByToken(req: express.Request) {
    const user = await this.getUserByToken(req);
    if (user) {
      return user.id;
    }
    return null;
  }

  async getUserList() {
    const { data } = await axios.get<IUser[]>(`${this.config.baseUrl}/users/users`);
    return data ?? null;
  }

  async getAllRoles(): Promise<IUserRole[]> {
    const { data } = await axios.get<IUserRole[]>(`${this.config.baseUrl}/users/roles`);
    return data ?? [];
  }

  async getUserRoles(userId: string) {
    const user = await this.getUserById(userId);
    const roles = await this.getAllRoles();
    const userRoleIds = Object.values(user.roles).reduce((acc, roleIds) => [...acc, ...roleIds], []);
    return roles
      .filter((role) => userRoleIds.includes(role.id))
      .map((role) => role.name);
  }

  async updateMobileUser(userId: string, keyValue: { [key: string]: string | number | boolean }) {
    await UserMobileAccess.findOneAndUpdate({ userId }, keyValue, { upsert: true });
  }

  async getUserAccess(userId: string): Promise<IUserAccess> {
    const foundUser = await this.getUserById(userId);
    const borrowersAccess = foundUser.borrowersAccess;
    const complianceBorrowersAccess =
      (await BorrowerCompliance.find({ 'borrower': { $in: borrowersAccess } }))
        .map((b) => b._id.toString());
    return { allBorrowers: foundUser.allBorrowers, borrowersAccess, complianceBorrowersAccess };
  }

  async getUserLogByDate(userId: string, date: Date) {
    return UserLog.findOne({ userId, timestamp: date }).lean();
  }
}
