const db = require("../models");
const Employee = db.employee;
const ExtUserPermission = db.ext_usr_perm;
const config = require("../config/auth.config");
const axios = require("axios");

var jwt = require("jsonwebtoken");
var bcrypt = require("bcryptjs");
const { splitSite } = require("../middlewares");
const HR_AUTH_LOGIN_ENDPOINT = "https://ws-hr.mayosolution.com/api/auth/login";
const SYSTEM_ACCESS_PERMISSION = "System Access";

const ALL_PERMISSIONS = [
  'Dashboard',
  'Admin View',
  'View_Info Screen',
  'Edit_Info Screen',
  'View_Customer Info _Personal Info',
  'View_Customer Info _Care & Services',
  'View_Customer Info _Medical & Insurance',
  'View_Customer Info _Confidential Details',
  'View_Customer Info _Form Submission',
  'Edit_Customer Info _ Personal Info',
  'Edit_Customer Info _ Care & Services',
  'Edit_Customer Info _ Medical & Insurance',
  'Edit_Customer Info _ Confidential Details',
  'Edit_Customer Info _ Form Submission',
  'Discharge_Customer',
  'Reactivate_Customer',
  'Create_Customer',
  'Export_Customer Report',
  'View _Calendar _Medical Appointment',
  'View _Calendar _Activities',
  'View _Calendar _Attendance Notes',
  'View _Calendar _Meal Plan',
  'View _Calendar _Important Dates',
  'Edit&Create _Calendar _Medical Appointment',
  'Edit&Create _Calendar _Activities',
  'Edit&Create _Calendar _Attendance Notes',
  'Edit&Create _Calendar _Meal Plan',
  'Edit&Create _Calendar _Important Dates',
  'View_Messaging',
  'Sent_Messaging',
  'View_Messaging Template',
  'Create&Edit_Messaging Template',
  'View_Vehicle info_Basic Info',
  'View_Vehicle info_Documents',
  'View_Vehicle info_Repair Records',
  'Edit_Vehicle info_Basic Info',
  'Edit_Vehicle info_Documents',
  'Edit_Vehicle info_Repair Records',
  'Add_New Vehicle',
  'Archive_Vehicle',
  'Export_Vehicle Report',
  'View_Transportation Schedule_Route Overview',
  'Create&Edit_Transportation Schedule',
  'Export_Transportation Schedule Report',
  'View_Route Template',
  'Create&Edit_Route Template',
  'View_Driver Assignment for Appointment',
  'Edit_Driver Assignment for Appointment',
  'isDriver',
  'View_Provider Info',
  'Create & Edit _Provider Info',
  'View_Appointment Request',
  'Edit & Create_Appointment Request',
  'View_Appointment Calendar',
  'Edit & Create_Appointment Calendar',
  'Medical Template',
  'View_Meal Status',
  'Edit_Meal Status',
  'View_Seating Chart',
  'Edit_Seating Chart',
  'Employee page',
  'Set Permission for Employee'
];

const getEffectivePermissions = (employeeDoc) => {
  const username = `${employeeDoc?.username || ''}`.trim().toLowerCase();
  const roles = Array.isArray(employeeDoc?.roles) ? employeeDoc.roles : [];
  const permissions = Array.isArray(employeeDoc?.permissions) ? employeeDoc.permissions : [];
  // Keep hardcoded full permission override.
  if (username === 'testadmin03') return ALL_PERMISSIONS;
  // Backward-compatible fallback for old admin records with no permissions assigned yet.
  if (permissions.length === 0 && roles.includes('admin')) return ALL_PERMISSIONS;
  return permissions;
};

const isEmployeeActive = (employeeDoc) => {
  const rawStatus = employeeDoc?.status;
  // Backward compatibility: older records may not have status set.
  if (rawStatus === undefined || rawStatus === null || `${rawStatus}`.trim() === "") {
    return true;
  }
  return `${rawStatus}`.trim().toLowerCase() === "active";
};

const parseExternalTokenId = (value = "") => {
  const token = `${value || ""}`;
  if (!token.startsWith("ext:")) return null;
  const chunks = token.split(":");
  if (chunks.length < 3) return null;
  return {
    externalUserId: chunks[1],
    site: Number(chunks[2]) || null
  };
};

const getExternalPermissionBySite = async (externalUserId, site) => {
  if (!externalUserId || !site) return null;
  return ExtUserPermission.findOne({
    external_user_id: `${externalUserId}`,
    allow_site: Number(site)
  }).sort({ updatedAt: -1 });
};

const buildExternalAuthResponse = async (externalUser, site) => {
  const permissionDoc = await getExternalPermissionBySite(externalUser?.id, site);
  const permissions = Array.isArray(permissionDoc?.permissions) ? permissionDoc.permissions : [];
  const tokenPayload = {
    id: `ext:${externalUser?.id}:${site}`,
    authSource: "external",
    externalUserId: externalUser?.id,
    site: Number(site),
    username: externalUser?.username || "",
    email: externalUser?.email || "",
    name: externalUser?.name || "",
    firstname: externalUser?.firstname || "",
    lastname: externalUser?.lastname || "",
    status: externalUser?.status || ""
  };
  const accessToken = jwt.sign(tokenPayload, config.secret, {
    expiresIn: 86400
  });

  return {
    accessToken,
    username: externalUser?.username || "",
    email: externalUser?.email || "",
    roles: [],
    permissions,
    id: externalUser?.id,
    name: externalUser?.name || "",
    firstname: externalUser?.firstname || "",
    lastname: externalUser?.lastname || "",
    site: Number(site),
    status: externalUser?.status || "",
    auth_source: "external"
  };
};

// Create and Save a new User
exports.login = async (req, res) => {
  const emailUsername = req.body.emailUsername;
  const password = req.body.password;
  const requestedSite = Number(req.body.site);
  const site = Number.isInteger(requestedSite) && requestedSite > 0 ? requestedSite : splitSite.findSiteNumber(req);

  if (!emailUsername) {
    return res.status(400).send({ message: "email or username is required" });
  }

  try {
    let condition = {
      $or: [{ email: emailUsername }, { username: emailUsername }]
    };
    condition = splitSite.splitSiteGet(req, condition);
    const localEmployees = await Employee.find(condition);

    if (localEmployees && localEmployees.length > 0) {
      const localEmployee = localEmployees[0];
      const activeEmployee = isEmployeeActive(localEmployee);
      const isPasswordCorrect = bcrypt.compareSync(password, localEmployee.password || "");
      if (localEmployees.length === 1 && isPasswordCorrect && activeEmployee) {
        const token = jwt.sign({ id: localEmployee.id }, config.secret, {
          expiresIn: 86400 // 24 hours
        });
        return res.send({
          accessToken: token,
          username: localEmployee.username,
          email: localEmployee.email,
          roles: localEmployee.roles,
          permissions: getEffectivePermissions(localEmployee),
          id: localEmployee.id,
          name: localEmployee.name,
          name_cn: localEmployee.name_cn
        });
      }
      if (!activeEmployee) {
        throw Error("User is not activated");
      }
      // Local account exists but credential doesn't match. Continue to external auth fallback.
    }

    try {
      const externalAuthResponse = await axios.post(
        HR_AUTH_LOGIN_ENDPOINT,
        {
          emailUsername,
          password,
          site
        },
        { timeout: 15000 }
      );
      const externalUser = externalAuthResponse?.data;
      const isExternalUserValid =
        externalUser &&
        externalUser.id &&
        `${externalUser?.status || ""}`.trim().toLowerCase() === "active";
      if (isExternalUserValid) {
        const loginPayload = await buildExternalAuthResponse(externalUser, site);
        return res.send(loginPayload);
      }
    } catch (_externalError) {
      // Fall through to existing invalid-login response.
    }

    throw Error("Email or Password Is Invalid");
  } catch (err) {
    return res.status(500).send({
      message: err.message || "Email Or Password Invalid"
    });
  }
};

exports.me = (req, res) => {
  const authPayload = req.authPayload || {};
  const parsedExternalToken = parseExternalTokenId(req.userId);
  const isExternalAuth =
    authPayload?.authSource === "external" || !!parsedExternalToken?.externalUserId;

  if (isExternalAuth) {
    const site = Number(authPayload?.site) || parsedExternalToken?.site || splitSite.findSiteNumber(req);
    const externalUserId = authPayload?.externalUserId || parsedExternalToken?.externalUserId;
    return getExternalPermissionBySite(externalUserId, site)
      .then((permissionDoc) => {
        let permissions = Array.isArray(permissionDoc?.permissions) ? permissionDoc.permissions : [];
        if (permissions.includes(SYSTEM_ACCESS_PERMISSION) === false) {
          // Keep login valid; UI permission gate will lock down non-system users.
          permissions = permissions.filter(Boolean);
        }
        return res.send({
          username: authPayload?.username || "",
          email: authPayload?.email || "",
          roles: [],
          permissions,
          id: externalUserId,
          name: authPayload?.name || "",
          firstname: authPayload?.firstname || "",
          lastname: authPayload?.lastname || "",
          site,
          status: authPayload?.status || "",
          auth_source: "external"
        });
      })
      .catch((err) => {
        return res.status(500).send({
          message: err.message || "Failed to fetch current user"
        });
      });
  }

  const condition = splitSite.splitSiteGet(req, { _id: req.userId });
  Employee.findOne(condition)
    .then((employee) => {
      if (!employee) {
        return res.status(404).send({ message: "User not found" });
      }
      if (!isEmployeeActive(employee)) {
        return res.status(403).send({ message: "User is not activated" });
      }
      return res.send({
        username: employee.username,
        email: employee.email,
        roles: employee.roles,
        permissions: getEffectivePermissions(employee),
        id: employee.id,
        name: employee.name,
        name_cn: employee.name_cn
      });
    })
    .catch((err) => {
      res.status(500).send({
        message: err.message || "Failed to fetch current user"
      });
    });
};