const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const db = require('../models');
const config = require('../config');
const { sendResponse } = require('../utils/response');
const asyncHandler = require('../middlewares/asyncHandler');

const User = db.User;
const Company = db.Company;
const Role = db.Role;
const Permission = db.Permission;
const UserRole = db.UserRole;
const RolePermission = db.RolePermission;
const Employee = db.Employee;

const generateTokens = (user, companyId = null) => {
  const payload = {
    userId: user.id,
    companyId: companyId || user.company_id,
    username: user.username,
  };

  const accessToken = jwt.sign(payload, config.jwt.secret, {
    expiresIn: config.jwt.expiresIn,
  });

  const refreshToken = jwt.sign(payload, config.jwt.refreshSecret, {
    expiresIn: config.jwt.refreshExpiresIn,
  });

  return { accessToken, refreshToken };
};

exports.login = asyncHandler(async (req, res) => {
  const { companyCode, userId, password } = req.body;

  if (!userId || !password) {
    return sendResponse(res, 400, false, 'User ID and Password are required', null);
  }

  let company = null;
  let user = null;

  if (companyCode) {
    company = await Company.findOne({
      where: { code: companyCode, status: 'ACTIVE' },
    });

    if (!company) {
      return sendResponse(res, 401, false, 'Invalid Company Code', null);
    }

    user = await User.findOne({
      where: {
        username: userId,
        company_id: company.id,
        is_active: true,
        status: 'ACTIVE',
      },
      include: [
        {
          model: Employee,
          as: 'employee',
          attributes: ['id', 'employee_number', 'first_name', 'last_name'],
        },
      ],
    });
  }

  if (!user) {
    user = await User.findOne({
      where: {
        username: userId,
        is_active: true,
        status: 'ACTIVE',
      },
      include: [
        {
          model: Company,
          as: 'company',
          attributes: ['id', 'code', 'name'],
        },
        {
          model: Employee,
          as: 'employee',
          attributes: ['id', 'employee_number', 'first_name', 'last_name'],
        },
      ],
    });

    if (user) {
      if (user.is_super_admin) {
        if (user.company) {
          company = user.company;
        }
      } else {
        if (!companyCode) {
          return sendResponse(res, 401, false, 'Company Code is required', null);
        }
        user = null;
      }
    }
  }

  if (!user) {
    return sendResponse(res, 401, false, 'Invalid User ID or Password', null);
  }

  if (user.is_locked) {
    return sendResponse(res, 401, false, 'Account is locked. Please contact administrator', null);
  }

  const isPasswordValid = await bcrypt.compare(password, user.password_hash);

  if (!isPasswordValid) {
    return sendResponse(res, 401, false, 'Invalid User ID or Password', null);
  }

  const userWithRoles = await User.findByPk(user.id, {
    include: [
      {
        model: Role,
        as: 'roles',
        through: { attributes: [] },
        include: [
          {
            model: Permission,
            as: 'permissions',
            through: { where: { status: 'ACTIVE' }, attributes: [] },
          },
        ],
      },
    ],
  });

  const roles = (userWithRoles.roles || []).map((role) => ({
    id: role.id,
    roleCode: role.role_code,
    roleName: role.role_name,
  }));

  const permissionsMap = new Map();
  (userWithRoles.roles || []).forEach((role) => {
    (role.permissions || []).forEach((permission) => {
      if (!permissionsMap.has(permission.id)) {
        permissionsMap.set(permission.id, {
          id: permission.id,
          permissionCode: permission.permission_code,
          permissionName: permission.permission_name,
          module: permission.module,
          resource: permission.resource,
          action: permission.action,
        });
      }
    });
  });
  const permissions = Array.from(permissionsMap.values());

  const { accessToken, refreshToken } = generateTokens(user, company ? company.id : null);

  const userProfile = {
    id: user.id,
    username: user.username,
    email: user.email,
    firstName: user.first_name,
    lastName: user.last_name,
    isSuperAdmin: user.is_super_admin,
    companyId: user.company_id,
    companyCode: company ? company.code : null,
    companyName: company ? company.name : null,
  };

  sendResponse(res, 200, true, 'Login successful', {
    accessToken,
    refreshToken,
    user: userProfile,
    roles,
    permissions,
  });
});

exports.logout = asyncHandler(async (req, res) => {
  sendResponse(res, 200, true, 'Logout successful', null);
});

exports.refreshToken = asyncHandler(async (req, res) => {
  const { refreshToken } = req.body;

  if (!refreshToken) {
    return sendResponse(res, 400, false, 'Refresh token is required', null);
  }

  try {
    const decoded = jwt.verify(refreshToken, config.jwt.refreshSecret);

    const user = await User.findByPk(decoded.userId, {
      where: { is_active: true, status: 'ACTIVE' },
    });

    if (!user) {
      return sendResponse(res, 401, false, 'Invalid refresh token', null);
    }

    const { accessToken } = generateTokens(user);

    sendResponse(res, 200, true, 'Token refreshed successfully', {
      accessToken,
    });
  } catch (error) {
    return sendResponse(res, 401, false, 'Invalid or expired refresh token', null);
  }
});

exports.getProfile = asyncHandler(async (req, res) => {
  const userId = req.user.userId;

  const user = await User.findByPk(userId, {
    include: [
      {
        model: Company,
        as: 'company',
        attributes: ['id', 'code', 'name'],
      },
      {
        model: Employee,
        as: 'employee',
        attributes: ['id', 'employee_number', 'first_name', 'last_name'],
      },
      {
        model: Role,
        as: 'roles',
        through: { attributes: [] },
        include: [
          {
            model: Permission,
            as: 'permissions',
            through: { where: { status: 'ACTIVE' }, attributes: [] },
          },
        ],
      },
    ],
  });

  if (!user) {
    return sendResponse(res, 404, false, 'User not found', null);
  }

  const roles = user.roles.map((role) => ({
    id: role.id,
    roleCode: role.role_code,
    roleName: role.role_name,
  }));

  const permissionsMap = new Map();
  (user.roles || []).forEach((role) => {
    (role.permissions || []).forEach((permission) => {
      if (!permissionsMap.has(permission.id)) {
        permissionsMap.set(permission.id, {
          id: permission.id,
          permissionCode: permission.permission_code,
          permissionName: permission.permission_name,
          module: permission.module,
          resource: permission.resource,
          action: permission.action,
        });
      }
    });
  });
  const permissions = Array.from(permissionsMap.values());

  const userProfile = {
    id: user.id,
    username: user.username,
    email: user.email,
    firstName: user.first_name,
    lastName: user.last_name,
    isSuperAdmin: user.is_super_admin,
    companyId: user.company_id,
    company: user.company
      ? {
          id: user.company.id,
          code: user.company.code,
          name: user.company.name,
        }
      : null,
    employee: user.employee
      ? {
          id: user.employee.id,
          employeeNumber: user.employee.employee_number,
          firstName: user.employee.first_name,
          lastName: user.employee.last_name,
        }
      : null,
    roles,
    permissions,
  };

  sendResponse(res, 200, true, 'Profile retrieved successfully', userProfile);
});

