const invitesService = require("../services/group-invites.service");
const { sendError } = require("../utils/http");
const { logError } = require("../utils/logger");
const { inviteCodeLast4 } = require("../utils/redaction");

function getClientIp(req) {
  const forwardedFor = req.headers["x-forwarded-for"];
  if (typeof forwardedFor === "string" && forwardedFor.trim()) {
    return forwardedFor.split(",")[0].trim();
  }
  return req.ip || req.socket?.remoteAddress || null;
}

function parseRequestedGroupId(req) {
  const headerGroupId = req.headers["x-group-id"] || req.headers["x-household-id"];
  if (headerGroupId) {
    const raw = Array.isArray(headerGroupId) ? headerGroupId[0] : headerGroupId;
    return raw;
  }
  if (req.query?.groupId !== undefined) {
    return req.query.groupId;
  }
  if (req.body?.groupId !== undefined) {
    return req.body.groupId;
  }
  return undefined;
}

function clampTtlDays(value) {
  const parsed = Number.parseInt(value, 10);
  if (!Number.isInteger(parsed)) return 1;
  return Math.max(1, Math.min(7, parsed));
}

function mapServiceError(req, res, error, context, extraLog = {}) {
  if (error instanceof invitesService.InviteServiceError) {
    return sendError(res, error.statusCode, error.message, error.code);
  }
  logError(req, context, error, extraLog);
  return sendError(res, 500, "Failed to process invite request");
}

exports.listInviteLinks = async (req, res) => {
  try {
    const requestedGroupId = parseRequestedGroupId(req);
    const groupId = await invitesService.resolveManagedGroupId(
      req.user.id,
      requestedGroupId
    );
    const links = await invitesService.listInviteLinks(req.user.id, groupId);
    res.json({ links });
  } catch (error) {
    return mapServiceError(req, res, error, "groupInvites.listInviteLinks");
  }
};

exports.createInviteLink = async (req, res) => {
  try {
    const requestedGroupId = parseRequestedGroupId(req);
    const groupId = await invitesService.resolveManagedGroupId(
      req.user.id,
      requestedGroupId
    );
    const ttlDays = clampTtlDays(req.body?.ttlDays);
    const expiresAt = new Date(Date.now() + ttlDays * 24 * 60 * 60 * 1000);
    const link = await invitesService.createInviteLink(
      req.user.id,
      groupId,
      req.body?.policy,
      Boolean(req.body?.singleUse),
      expiresAt,
      req.request_id,
      getClientIp(req),
      req.headers["user-agent"] || null
    );
    res.status(201).json({ link });
  } catch (error) {
    return mapServiceError(req, res, error, "groupInvites.createInviteLink");
  }
};

exports.revokeInviteLink = async (req, res) => {
  try {
    const requestedGroupId = parseRequestedGroupId(req);
    const groupId = await invitesService.resolveManagedGroupId(
      req.user.id,
      requestedGroupId
    );
    await invitesService.revokeInviteLink(
      req.user.id,
      groupId,
      req.body?.linkId,
      req.request_id,
      getClientIp(req),
      req.headers["user-agent"] || null
    );
    res.json({ ok: true });
  } catch (error) {
    return mapServiceError(req, res, error, "groupInvites.revokeInviteLink");
  }
};

exports.reviveInviteLink = async (req, res) => {
  try {
    const requestedGroupId = parseRequestedGroupId(req);
    const groupId = await invitesService.resolveManagedGroupId(
      req.user.id,
      requestedGroupId
    );
    const ttlDays = clampTtlDays(req.body?.ttlDays);
    const expiresAt = new Date(Date.now() + ttlDays * 24 * 60 * 60 * 1000);
    await invitesService.reviveInviteLink(
      req.user.id,
      groupId,
      req.body?.linkId,
      expiresAt,
      req.request_id,
      getClientIp(req),
      req.headers["user-agent"] || null
    );
    res.json({ ok: true });
  } catch (error) {
    return mapServiceError(req, res, error, "groupInvites.reviveInviteLink");
  }
};

exports.deleteInviteLink = async (req, res) => {
  try {
    const requestedGroupId = parseRequestedGroupId(req);
    const groupId = await invitesService.resolveManagedGroupId(
      req.user.id,
      requestedGroupId
    );
    await invitesService.deleteInviteLink(
      req.user.id,
      groupId,
      req.body?.linkId,
      req.request_id,
      getClientIp(req),
      req.headers["user-agent"] || null
    );
    res.json({ ok: true });
  } catch (error) {
    return mapServiceError(req, res, error, "groupInvites.deleteInviteLink");
  }
};

exports.getJoinPolicy = async (req, res) => {
  try {
    const requestedGroupId = parseRequestedGroupId(req);
    const groupId = await invitesService.resolveManagedGroupId(
      req.user.id,
      requestedGroupId
    );
    const joinPolicy = await invitesService.getGroupJoinPolicy(req.user.id, groupId);
    res.json({ joinPolicy });
  } catch (error) {
    return mapServiceError(req, res, error, "groupInvites.getJoinPolicy");
  }
};

exports.setJoinPolicy = async (req, res) => {
  try {
    const requestedGroupId = parseRequestedGroupId(req);
    const groupId = await invitesService.resolveManagedGroupId(
      req.user.id,
      requestedGroupId
    );
    await invitesService.setGroupJoinPolicy(
      req.user.id,
      groupId,
      req.body?.joinPolicy,
      req.request_id,
      getClientIp(req),
      req.headers["user-agent"] || null
    );
    res.json({ ok: true });
  } catch (error) {
    return mapServiceError(req, res, error, "groupInvites.setJoinPolicy");
  }
};

exports.getInviteLinkSummary = async (req, res) => {
  const token = req.params.token;
  const inviteLast4 = inviteCodeLast4(token);
  try {
    const link = await invitesService.getInviteLinkSummaryByToken(
      token,
      req.user?.id || null
    );
    res.json({ link });
  } catch (error) {
    return mapServiceError(req, res, error, "groupInvites.getInviteLinkSummary", {
      invite_last4: inviteLast4,
    });
  }
};

exports.acceptInviteLink = async (req, res) => {
  const token = req.params.token;
  const inviteLast4 = inviteCodeLast4(token);
  try {
    const result = await invitesService.acceptInviteLink(
      req.user.id,
      token,
      req.request_id,
      getClientIp(req),
      req.headers["user-agent"] || null
    );
    res.json({ result });
  } catch (error) {
    return mapServiceError(req, res, error, "groupInvites.acceptInviteLink", {
      invite_last4: inviteLast4,
    });
  }
};
