import { Types } from "mongoose";
import UserBlockModel from "../models/UserBlockModel";
import UserModel from "../models/UserModel";
import { PaginatedResult } from "./connectionService";
import { buildImageUrl } from "../utils/imageUrl";

export async function isBlockedEitherWay(
  userA: string,
  userB: string,
): Promise<boolean> {
  if (userA === userB) return false;

  const a = new Types.ObjectId(userA);
  const b = new Types.ObjectId(userB);

  const existing = await UserBlockModel.exists({
    $or: [
      { blockerId: a, blockedId: b },
      { blockerId: b, blockedId: a },
    ],
  });

  return Boolean(existing);
}

export async function assertNotBlocked(
  userA: string,
  userB: string,
): Promise<void> {
  const blocked = await isBlockedEitherWay(userA, userB);
  if (blocked) throw new Error("BLOCKED");
}

export async function blockUser(
  blockerId: string,
  blockedUserId: string,
): Promise<Record<string, unknown>> {
  if (blockerId === blockedUserId) {
    throw new Error("CANNOT_SELF_BLOCK");
  }

  const target = await UserModel.findOne({
    _id: new Types.ObjectId(blockedUserId),
    isDeleted: false,
  })
    .select("_id fullName email image")
    .lean();

  if (!target) {
    throw new Error("USER_NOT_FOUND");
  }

  const already = await UserBlockModel.findOne({
    blockerId: new Types.ObjectId(blockerId),
    blockedId: new Types.ObjectId(blockedUserId),
  }).lean();

  if (already) {
    throw new Error("ALREADY_BLOCKED");
  }

  const block = await UserBlockModel.create({
    blockerId: new Types.ObjectId(blockerId),
    blockedId: new Types.ObjectId(blockedUserId),
  });

  return {
    _id: block._id,
    blockedUser: {
      _id: target._id,
      fullName: target.fullName ?? "",
      email: target.email ?? "",
      image: target.image ? buildImageUrl(target.image) : null,
    },
    createdAt: block.createdAt,
  };
}

export async function unblockUser(
  blockerId: string,
  blockedUserId: string,
): Promise<void> {
  const result = await UserBlockModel.deleteOne({
    blockerId: new Types.ObjectId(blockerId),
    blockedId: new Types.ObjectId(blockedUserId),
  });

  if (result.deletedCount === 0) {
    throw new Error("NOT_BLOCKED");
  }
}

export async function listBlockedUsers(
  blockerId: string,
  page: number,
  limit: number,
): Promise<PaginatedResult<Record<string, unknown>>> {
  const skip = (page - 1) * limit;
  const filter = { blockerId: new Types.ObjectId(blockerId) };

  const [total, rows] = await Promise.all([
    UserBlockModel.countDocuments(filter),
    UserBlockModel.find(filter)
      .sort({ createdAt: -1 })
      .skip(skip)
      .limit(limit)
      .populate("blockedId", "fullName email image")
      .lean(),
  ]);

  const data = rows.map((row) => {
    const blocked = row.blockedId as unknown as {
      _id: Types.ObjectId;
      fullName?: string;
      email?: string;
      image?: string | null;
    } | null;

    return {
      _id: row._id,
      blockedUser: blocked
        ? {
            _id: blocked._id,
            fullName: blocked.fullName ?? "",
            email: blocked.email ?? "",
            image: blocked.image ? buildImageUrl(blocked.image) : null,
          }
        : null,
      createdAt: row.createdAt,
    };
  });

  return {
    data,
    total,
    page,
    totalPages: Math.ceil(total / limit) || 0,
  };
}
