import { Prop, Schema, SchemaFactory, raw } from '@nestjs/mongoose';
import { HydratedDocument, Types } from 'mongoose';
import { User } from '../../users/schemas/user.schema';

export type ConversationDocument = HydratedDocument<Conversation>;

const hiddenForSchema = raw({
  userId: { type: Types.ObjectId, ref: User.name, required: true },
  hiddenAt: { type: Date, required: true },
});

@Schema({ timestamps: true, versionKey: false })
export class Conversation {
  @Prop({ type: [Types.ObjectId], ref: User.name, required: true, index: true })
  participantIds!: Types.ObjectId[];

  @Prop({ default: false, index: true })
  isGroup!: boolean;

  @Prop({ default: '', maxlength: 120, trim: true })
  title!: string;

  @Prop({ type: Types.ObjectId, ref: User.name, required: false, index: true })
  createdBy?: Types.ObjectId;

  @Prop({ type: Types.ObjectId, required: false, index: true })
  lastMessageId?: Types.ObjectId;

  @Prop({ default: '', maxlength: 4000 })
  lastMessageText!: string;

  @Prop({ type: Date, required: false, index: true })
  lastMessageAt?: Date;

  @Prop({ type: Map, of: Number, default: {} })
  unreadCountByUser!: Map<string, number>;

  @Prop({ type: [hiddenForSchema], default: [] })
  hiddenFor!: Array<{ userId: Types.ObjectId; hiddenAt: Date }>;
}

export const ConversationSchema = SchemaFactory.createForClass(Conversation);
ConversationSchema.index({ participantIds: 1, updatedAt: -1 });
ConversationSchema.index({ lastMessageAt: -1, updatedAt: -1 });
ConversationSchema.index({ participantIds: 1, isGroup: 1, lastMessageAt: -1 });
ConversationSchema.index({ 'hiddenFor.userId': 1, lastMessageAt: -1 });

const normalizeUnreadCountByUser = (value: unknown): Record<string, number> => {
  if (!value) {
    return {};
  }

  if (value instanceof Map) {
    return Object.fromEntries(
      Array.from(value.entries()).map(([key, count]) => [key.toString(), Number(count) || 0]),
    );
  }

  if (typeof value === 'object') {
    return Object.fromEntries(
      Object.entries(value as Record<string, unknown>)
        .filter(([key]) => !key.startsWith('$__') && key !== '$isMongooseMap')
        .map(([key, count]) => [key, Number(count) || 0]),
    );
  }

  return {};
};

const transformConversation = (_doc: unknown, ret: any) => {
  ret.unreadCountByUser = normalizeUnreadCountByUser(ret.unreadCountByUser);
  ret.hiddenFor = Array.isArray(ret.hiddenFor)
    ? ret.hiddenFor.map((item: any) => ({
        userId: item.userId?.toString?.() ?? item.userId,
        hiddenAt: item.hiddenAt ?? null,
      }))
    : [];
  return ret;
};

ConversationSchema.set('toJSON', { transform: transformConversation });
ConversationSchema.set('toObject', { transform: transformConversation });
