import { OAuth2Client } from "google-auth-library";
import passport from "passport";
import {
  getUserByEmail,
  findOrCreateUserByGoogle,
} from "../repositories/userRepository.js";
import { getJwtSecret } from "../config/jwtSecret.js";
import {
  assertLoginToken,
  isLaunchAppId,
  JWT_PURPOSE,
  signLaunchToken,
  signLoginToken,
  TokenClaimError,
  userClaimsFromPayload,
  verifyToken,
} from "../../../shared/hse-jwt/index.js";

const FRONTEND_GOOGLE_CLIENT_ID =
  "932347645492-2bl00q52tkh3sd8mc5bq1p7kqrarp2gc.apps.googleusercontent.com";

function roleNamesAndIds(user) {
  const roles = user.roles || [];
  const roleNames = roles.map(
    (role) => (role && (role.role_name ?? role.roleName)) || ""
  );
  const roleIds = roles
    .map((role) =>
      (role && (role.role_id ?? role.roleId)) != null
        ? Number(role.role_id ?? role.roleId)
        : null
    )
    .filter((id) => id != null);
  return { roleNames, roleIds };
}

export function generateToken(user) {
  const { roleNames, roleIds } = roleNamesAndIds(user);
  return signLoginToken(
    {
      userId: user.user_id,
      email: user.email_id,
      employeeName: user.employee_name,
      roles: roleNames,
      roleIds,
    },
    getJwtSecret()
  );
}

function formatUserResponse(user) {
  const roles = user.roles || [];
  return {
    userId: user.user_id,
    employeeName: user.employee_name,
    email: user.email_id,
    allRoles: roles.map((role) => ({
      roleId: role.role_id,
      roleName: role.role_name,
    })),
  };
}

export async function appLaunch(req, res) {
  try {
    const app = String(req.body?.app ?? "")
      .trim()
      .toLowerCase();
    if (!isLaunchAppId(app)) {
      return res.status(400).json({
        success: false,
        message: "Invalid app. Use one of: sat, risk, swi, ptw",
      });
    }

    const userClaims = userClaimsFromPayload(req.user);
    if (userClaims.userId == null) {
      return res.status(401).json({
        success: false,
        message: "Login token required",
      });
    }

    const token = signLaunchToken(userClaims, app, getJwtSecret());
    return res.status(200).json({
      success: true,
      app,
      token,
      tokenType: "Bearer",
      expiresIn: process.env.JWT_LAUNCH_EXPIRES_IN || "60s",
    });
  } catch (error) {
    return res.status(500).json({
      success: false,
      message: "Failed to issue launch token",
      error: error.message,
    });
  }
}

/**
 * Exchange a child-app session/launch JWT for a Master login JWT so the user
 * can open Master routes (e.g. personal dashboard) without signing in again.
 */
export async function appReturn(req, res) {
  try {
    const authHeader = req.headers.authorization || "";
    const [scheme, bearer] = authHeader.split(" ");
    if (scheme !== "Bearer" || !bearer) {
      return res.status(401).json({
        success: false,
        message: "Missing or invalid Authorization header",
      });
    }

    const secret = getJwtSecret();
    const decoded = verifyToken(bearer, secret);
    const purpose = decoded?.purpose;
    const aud = decoded?.aud;

    if (
      purpose === JWT_PURPOSE.APP_SESSION ||
      purpose === JWT_PURPOSE.APP_LAUNCH
    ) {
      if (!isLaunchAppId(aud)) {
        return res.status(401).json({
          success: false,
          message: "Unknown app audience",
        });
      }
    } else {
      try {
        assertLoginToken(decoded);
      } catch (error) {
        if (error instanceof TokenClaimError) {
          return res.status(error.status).json({
            success: false,
            message: error.message,
          });
        }
        return res.status(401).json({
          success: false,
          message: "Invalid token for app return",
        });
      }
      return res.status(200).json({
        success: true,
        token: bearer,
        tokenType: "Bearer",
      });
    }

    const userClaims = userClaimsFromPayload(decoded);
    if (userClaims.userId == null) {
      return res.status(401).json({
        success: false,
        message: "Token missing user identity",
      });
    }

    const token = signLoginToken(userClaims, secret);
    return res.status(200).json({
      success: true,
      token,
      tokenType: "Bearer",
      expiresIn: process.env.JWT_EXPIRES_IN || "7d",
    });
  } catch (error) {
    return res.status(401).json({
      success: false,
      message: "Invalid or expired token",
      error: error.message,
    });
  }
}

export async function login(req, res) {
  try {
    const { email, password } = req.body;

    if (!email || !password) {
      return res.status(400).json({
        success: false,
        message: "Email and password are required",
      });
    }

    const user = await getUserByEmail(email);
    if (!user) {
      return res.status(401).json({
        success: false,
        message: "Invalid email or password",
      });
    }

    const statusId = user.status_id != null ? Number(user.status_id) : null;
    if (statusId !== 1) {
      return res.status(403).json({
        success: false,
        message: "Account is inactive",
      });
    }

    if (user.password !== password) {
      return res.status(401).json({
        success: false,
        message: "Invalid email or password",
      });
    }

    const token = generateToken(user);
    return res.status(200).json({
      success: true,
      message: "Login successful",
      token,
      user: formatUserResponse(user),
    });
  } catch (error) {
    return res.status(500).json({
      success: false,
      message: "Internal server error",
      error: error.message,
    });
  }
}

export async function googleToken(req, res) {
  try {
    const { credential } = req.body;
    if (!credential) {
      return res.status(400).json({
        success: false,
        message: "Google credential is required",
      });
    }

    const clientId =
      process.env.GOOGLE_CLIENT_ID_WEB || FRONTEND_GOOGLE_CLIENT_ID;
    const client = new OAuth2Client(clientId);
    const ticket = await client.verifyIdToken({
      idToken: credential,
      audience: clientId,
    });
    const payload = ticket.getPayload();
    const { email, name, given_name, family_name, sub } = payload;

    const profile = {
      id: sub,
      emails: [{ value: email }],
      displayName:
        name ||
        [given_name, family_name].filter(Boolean).join(" ").trim() ||
        "Google User",
    };

    const user = await findOrCreateUserByGoogle(profile);
    if (user.status_id !== 1) {
      return res.status(403).json({
        success: false,
        message:
          "Account is inactive. Please contact administrator to activate your account.",
      });
    }

    const token = generateToken(user);
    return res.status(200).json({
      success: true,
      message: "Google login successful",
      token,
      user: formatUserResponse(user),
    });
  } catch (error) {
    return res.status(401).json({
      success: false,
      message: "Invalid Google credential or authentication failed",
      error: error.message,
    });
  }
}

function isGoogleOAuthRedirectConfigured() {
  return Boolean(
    process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET
  );
}

export function googleAuth(req, res, next) {
  if (!isGoogleOAuthRedirectConfigured()) {
    return res.status(503).json({
      success: false,
      message:
        "Google OAuth (redirect) is not configured. Set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET.",
    });
  }
  return passport.authenticate("google", {
    scope: ["profile", "email"],
  })(req, res, next);
}

export async function googleCallback(req, res) {
  try {
    if (!isGoogleOAuthRedirectConfigured()) {
      return res.status(503).json({
        success: false,
        message: "Google OAuth (redirect) is not configured.",
      });
    }

    passport.authenticate("google", { session: false }, async (err, user) => {
      if (err) {
        return res.status(500).json({
          success: false,
          message: "OAuth authentication failed",
          error: err.message,
        });
      }

      if (!user) {
        return res.status(401).json({
          success: false,
          message: "Google authentication failed",
        });
      }

      if (user.status_id !== 1) {
        return res.status(403).json({
          success: false,
          message: "Account is inactive.",
        });
      }

      const token = generateToken(user);
      const frontendUrl =
        process.env.FRONTEND_URL || "http://localhost:5174";
      return res.redirect(
        `${frontendUrl}/auth/callback?token=${token}&success=true`
      );
    })(req, res);
  } catch (error) {
    const frontendUrl = process.env.FRONTEND_URL || "http://localhost:5174";
    return res.redirect(
      `${frontendUrl}/auth/callback?success=false&error=${encodeURIComponent(error.message)}`
    );
  }
}

export async function googleCallbackJSON(req, res) {
  try {
    if (!isGoogleOAuthRedirectConfigured()) {
      return res.status(503).json({
        success: false,
        message: "Google OAuth (redirect) is not configured.",
      });
    }

    passport.authenticate("google", { session: false }, async (err, user) => {
      if (err) {
        return res.status(500).json({
          success: false,
          message: "OAuth authentication failed",
          error: err.message,
        });
      }

      if (!user) {
        return res.status(401).json({
          success: false,
          message: "Google authentication failed",
        });
      }

      if (user.status_id !== 1) {
        return res.status(403).json({
          success: false,
          message: "Account is inactive.",
        });
      }

      const token = generateToken(user);
      return res.status(200).json({
        success: true,
        message: "Google login successful",
        token,
        user: formatUserResponse(user),
      });
    })(req, res);
  } catch (error) {
    return res.status(500).json({
      success: false,
      message: "Internal server error",
      error: error.message,
    });
  }
}
