import { Response } from "express";
import { CustomRequest } from "../interfaces/auth";
import { STATUS_CODES } from "../constants/statusCodes";
import { CONNECTION_CONSTANTS, AUTH_CONSTANTS } from "../constants/messages";
import ResponseUtil from "../utils/Response/responseUtils";
import {
  inviteByEmailSchema,
  nearbyUsersSchema,
  connectionsListSchema,
  requestsListSchema,
  requestIdParamSchema,
  removeConnectionParamSchema,
} from "../validators/connectionValidator";
import {
  sendInviteOrRequest,
  getNearbyUsers,
  getMyConnections,
  getConnectionRequests,
  acceptConnectionRequest,
  rejectConnectionRequest,
  cancelConnectionRequest,
  removeConnection,
} from "../services/connectionService";

function handleServiceError(res: Response, err: unknown): void {
  if (!(err instanceof Error)) {
    ResponseUtil.errorResponse(
      res,
      STATUS_CODES.INTERNAL_SERVER_ERROR,
      "An unexpected error occurred",
    );
    return;
  }

  switch (err.message) {
    case "CANNOT_SELF_CONNECT":
      ResponseUtil.errorResponse(res, STATUS_CODES.BAD_REQUEST, CONNECTION_CONSTANTS.CANNOT_SELF_CONNECT);
      break;
    case "ALREADY_CONNECTED":
      ResponseUtil.errorResponse(res, STATUS_CODES.CONFLICT, CONNECTION_CONSTANTS.ALREADY_CONNECTED);
      break;
    case "REQUEST_ALREADY_PENDING":
      ResponseUtil.errorResponse(res, STATUS_CODES.CONFLICT, CONNECTION_CONSTANTS.REQUEST_ALREADY_PENDING);
      break;
    case "REQUEST_NOT_FOUND":
      ResponseUtil.errorResponse(res, STATUS_CODES.NOT_FOUND, CONNECTION_CONSTANTS.REQUEST_NOT_FOUND);
      break;
    case "CONNECTION_NOT_FOUND":
      ResponseUtil.errorResponse(res, STATUS_CODES.NOT_FOUND, CONNECTION_CONSTANTS.CONNECTION_NOT_FOUND);
      break;
    case "FORBIDDEN":
      ResponseUtil.errorResponse(res, STATUS_CODES.FORBIDDEN, CONNECTION_CONSTANTS.FORBIDDEN);
      break;
    case "INVALID_STATUS":
      ResponseUtil.errorResponse(res, STATUS_CODES.CONFLICT, CONNECTION_CONSTANTS.INVALID_STATUS);
      break;
    case "USER_BANNED":
      ResponseUtil.errorResponse(res, STATUS_CODES.FORBIDDEN, AUTH_CONSTANTS.ACCOUNT_BANNED_ERROR);
      break;
    default:
      ResponseUtil.errorResponse(res, STATUS_CODES.INTERNAL_SERVER_ERROR, err.message);
  }
}

function isServiceError(err: unknown): boolean {
  return (
    err instanceof Error &&
    [
      "REQUEST_NOT_FOUND",
      "CONNECTION_NOT_FOUND",
      "FORBIDDEN",
      "INVALID_STATUS",
    ].includes(err.message)
  );
}

export const inviteOrRequest = async (req: CustomRequest, res: Response) => {
  try {
    const { email } = await inviteByEmailSchema.parseAsync(req.body);
    const result = await sendInviteOrRequest(req.userId!, email);
    const message =
      result.type === "request_sent"
        ? CONNECTION_CONSTANTS.REQUEST_SENT
        : CONNECTION_CONSTANTS.INVITE_SENT;
    ResponseUtil.successResponse(res, STATUS_CODES.SUCCESS, {
      requestId: result.requestId,
      inviteType: result.type === "invite_sent" ? "email" : "user",
    }, message);
  } catch (err) {
    if (err instanceof Error && ["CANNOT_SELF_CONNECT", "ALREADY_CONNECTED", "REQUEST_ALREADY_PENDING", "USER_BANNED"].includes(err.message)) {
      handleServiceError(res, err);
    } else {
      ResponseUtil.handleError(res, err);
    }
  }
};

export const nearbyUsers = async (req: CustomRequest, res: Response) => {
  try {
    const { latitude, longitude, radius, page, limit } =
      await nearbyUsersSchema.parseAsync(req.query);
    const result = await getNearbyUsers(
      req.userId!,
      latitude,
      longitude,
      radius,
      page,
      limit,
    );
    ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      result,
      CONNECTION_CONSTANTS.NEARBY_FETCHED,
    );
  } catch (err) {
    ResponseUtil.handleError(res, err);
  }
};

export const myConnections = async (req: CustomRequest, res: Response) => {
  try {
    const { search, page, limit } = await connectionsListSchema.parseAsync(req.query);
    const result = await getMyConnections(req.userId!, search, page, limit);
    ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      result,
      CONNECTION_CONSTANTS.CONNECTIONS_FETCHED,
    );
  } catch (err) {
    ResponseUtil.handleError(res, err);
  }
};

export const connectionRequests = async (req: CustomRequest, res: Response) => {
  try {
    const { type, page, limit } = await requestsListSchema.parseAsync(req.query);
    const result = await getConnectionRequests(req.userId!, type, page, limit);
    ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      result,
      CONNECTION_CONSTANTS.REQUESTS_FETCHED,
    );
  } catch (err) {
    ResponseUtil.handleError(res, err);
  }
};

export const acceptRequest = async (req: CustomRequest, res: Response) => {
  try {
    const { requestId } = await requestIdParamSchema.parseAsync(req.params);
    await acceptConnectionRequest(requestId, req.userId!);
    ResponseUtil.successResponse(res, STATUS_CODES.SUCCESS, {}, CONNECTION_CONSTANTS.REQUEST_ACCEPTED);
  } catch (err) {
    if (isServiceError(err)) handleServiceError(res, err);
    else ResponseUtil.handleError(res, err);
  }
};

export const rejectRequest = async (req: CustomRequest, res: Response) => {
  try {
    const { requestId } = await requestIdParamSchema.parseAsync(req.params);
    await rejectConnectionRequest(requestId, req.userId!);
    ResponseUtil.successResponse(res, STATUS_CODES.SUCCESS, {}, CONNECTION_CONSTANTS.REQUEST_REJECTED);
  } catch (err) {
    if (isServiceError(err)) handleServiceError(res, err);
    else ResponseUtil.handleError(res, err);
  }
};

export const cancelRequest = async (req: CustomRequest, res: Response) => {
  try {
    const { requestId } = await requestIdParamSchema.parseAsync(req.params);
    await cancelConnectionRequest(requestId, req.userId!);
    ResponseUtil.successResponse(res, STATUS_CODES.SUCCESS, {}, CONNECTION_CONSTANTS.REQUEST_CANCELLED);
  } catch (err) {
    if (isServiceError(err)) handleServiceError(res, err);
    else ResponseUtil.handleError(res, err);
  }
};

export const deleteConnection = async (req: CustomRequest, res: Response) => {
  try {
    const { userId } = await removeConnectionParamSchema.parseAsync(req.params);
    await removeConnection(userId, req.userId!);
    ResponseUtil.successResponse(res, STATUS_CODES.SUCCESS, {}, CONNECTION_CONSTANTS.CONNECTION_REMOVED);
  } catch (err) {
    if (
      err instanceof Error &&
      err.message === "CANNOT_SELF_CONNECT"
    ) {
      handleServiceError(res, err);
    } else if (isServiceError(err)) {
      handleServiceError(res, err);
    } else {
      ResponseUtil.handleError(res, err);
    }
  }
};
