import { Response } from "express";
import { CustomRequest } from "../interfaces/auth";
import { STATUS_CODES } from "../constants/statusCodes";
import { CHAT_CONSTANTS } from "../constants/messages";
import ResponseUtil from "../utils/Response/responseUtils";
import {
  createConversationSchema,
  conversationListSchema,
  conversationIdParamSchema,
  messageListSchema,
} from "../validators/chatValidator";
import {
  getOrCreateConversation,
  listConversations,
  listMessages,
  buildChatMediaFromUploads,
  markConversationRead,
} from "../services/chatService";
import { buildImageUrl } from "../utils/imageUrl";

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_CHAT":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.BAD_REQUEST,
        CHAT_CONSTANTS.CANNOT_SELF_CHAT,
      );
      break;
    case "NOT_CONNECTED":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.FORBIDDEN,
        CHAT_CONSTANTS.NOT_CONNECTED,
      );
      break;
    case "BLOCKED":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.FORBIDDEN,
        CHAT_CONSTANTS.BLOCKED,
      );
      break;
    case "USER_NOT_FOUND":
      ResponseUtil.errorResponse(res, STATUS_CODES.NOT_FOUND, "User not found");
      break;
    case "NOT_FOUND":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.NOT_FOUND,
        CHAT_CONSTANTS.NOT_FOUND,
      );
      break;
    case "FORBIDDEN":
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.FORBIDDEN,
        CHAT_CONSTANTS.FORBIDDEN,
      );
      break;
    default:
      ResponseUtil.errorResponse(
        res,
        STATUS_CODES.INTERNAL_SERVER_ERROR,
        err.message,
      );
  }
}

/**
 * POST /api/v1/chat/conversations
 * Start or get an existing 1:1 conversation with an accepted connection.
 */
export const createOrGetConversation = async (
  req: CustomRequest,
  res: Response,
) => {
  try {
    const { userId: otherUserId } = await createConversationSchema.parseAsync(
      req.body,
    );
    const result = await getOrCreateConversation(req.userId!, otherUserId);
    ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      result,
      CHAT_CONSTANTS.CONVERSATION_CREATED,
    );
  } catch (err) {
    if (
      err instanceof Error &&
      [
        "CANNOT_SELF_CHAT",
        "NOT_CONNECTED",
        "USER_NOT_FOUND",
        "BLOCKED",
      ].includes(err.message)
    ) {
      handleServiceError(res, err);
    } else {
      ResponseUtil.handleError(res, err);
    }
  }
};

/**
 * GET /api/v1/chat/conversations
 */
export const getConversations = async (req: CustomRequest, res: Response) => {
  try {
    const { page, limit } = await conversationListSchema.parseAsync(req.query);
    const result = await listConversations(req.userId!, page, limit);
    ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      result,
      CHAT_CONSTANTS.CONVERSATIONS_FETCHED,
    );
  } catch (err) {
    ResponseUtil.handleError(res, err);
  }
};

/**
 * GET /api/v1/chat/conversations/:conversationId/messages
 */
export const getConversationMessages = async (
  req: CustomRequest,
  res: Response,
) => {
  try {
    const { conversationId } = await conversationIdParamSchema.parseAsync(
      req.params,
    );
    const { page, limit, before } = await messageListSchema.parseAsync(
      req.query,
    );
    const result = await listMessages(
      conversationId,
      req.userId!,
      page,
      limit,
      before,
    );
    ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      result,
      CHAT_CONSTANTS.MESSAGES_FETCHED,
    );
  } catch (err) {
    if (
      err instanceof Error &&
      ["NOT_FOUND", "FORBIDDEN"].includes(err.message)
    ) {
      handleServiceError(res, err);
    } else {
      ResponseUtil.handleError(res, err);
    }
  }
};

/**
 * POST /api/v1/chat/upload
 * Multipart field `media` (1–10 files). Returns URLs to pass into socket `send_message`.
 */
export const uploadChatMedia = async (req: CustomRequest, res: Response) => {
  try {
    const files = req.files as Express.Multer.File[] | undefined;
    if (!files || files.length === 0) {
      return ResponseUtil.errorResponse(
        res,
        STATUS_CODES.BAD_REQUEST,
        CHAT_CONSTANTS.NO_FILES,
      );
    }

    const media = (await buildChatMediaFromUploads(files)).map((m) => ({
      url: buildImageUrl(m.url) ?? m.url,
      storedPath: m.url,
      type: m.type,
      mimeType: m.mimeType ?? null,
      fileName: m.fileName ?? null,
    }));

    return ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      { media },
      CHAT_CONSTANTS.MEDIA_UPLOADED,
    );
  } catch (err) {
    ResponseUtil.handleError(res, err);
  }
};

/**
 * POST /api/v1/chat/conversations/:conversationId/read
 * Reset unreadCount for the logged-in user.
 */
export const markConversationAsRead = async (
  req: CustomRequest,
  res: Response,
) => {
  try {
    const { conversationId } = await conversationIdParamSchema.parseAsync(
      req.params,
    );
    const result = await markConversationRead(conversationId, req.userId!);
    ResponseUtil.successResponse(
      res,
      STATUS_CODES.SUCCESS,
      result,
      CHAT_CONSTANTS.MARKED_READ,
    );
  } catch (err) {
    if (
      err instanceof Error &&
      ["NOT_FOUND", "FORBIDDEN"].includes(err.message)
    ) {
      handleServiceError(res, err);
    } else {
      ResponseUtil.handleError(res, err);
    }
  }
};
