import { Prop, Schema, SchemaFactory, raw } from '@nestjs/mongoose';
import { HydratedDocument, Types } from 'mongoose';
import { ModerationStatus } from '../../../common/enums/moderation-status.enum';
import { PostType } from '../../../common/enums/post-type.enum';
import { PostVisibility } from '../../../common/enums/post-visibility.enum';
import { ProcessingStatus } from '../../../common/enums/processing-status.enum';
import { buildPostMediaResponse } from '../../../common/utils/post-media-response.util';
import {
  resolveManagedFileUrl,
  resolveManagedFileUrlRecord,
  resolveManagedFileUrlRecords,
  resolveManagedFileUrls,
} from '../../../common/utils/public-url.util';
import { buildWaveformPeakSet } from '../../../common/utils/waveform.util';
import { User } from '../../users/schemas/user.schema';

export type PostDocument = HydratedDocument<Post>;

export type PostMediaVariantSet = {
  originalUrl: string;
  lowUrl: string;
  mediumUrl: string;
  highUrl: string;
};

export type PostImageItem = {
  url: string;
  caption: string;
  altText: string;
  order: number;
};

const mediaVariantSetSchema = raw({
  originalUrl: { type: String, default: '' },
  lowUrl: { type: String, default: '' },
  mediumUrl: { type: String, default: '' },
  highUrl: { type: String, default: '' },
});

const imageItemSchema = raw({
  url: { type: String, default: '' },
  caption: { type: String, default: '' },
  altText: { type: String, default: '' },
  order: { type: Number, default: 0 },
});

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

  @Prop({ type: Types.ObjectId, ref: 'Post', default: null, index: true })
  repostOfPostId?: Types.ObjectId | null;

  @Prop({ type: Types.ObjectId, ref: 'Post', default: null, index: true })
  quoteOfPostId?: Types.ObjectId | null;

  @Prop({ default: '', trim: true, maxlength: 2200, required: true })
  content!: string;

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

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

  @Prop({ default: '' })
  videoUrl!: string;

  @Prop({ default: '' })
  hlsUrl!: string;

  @Prop({ default: '' })
  audioUrl!: string;

  @Prop({ type: Number, min: 1, max: 7200, default: null })
  durationSeconds!: number | null;

  @Prop({ default: '' })
  thumbnailUrl!: string;

  @Prop({ type: mediaVariantSetSchema, default: null })
  thumbnailVariants!: PostMediaVariantSet | null;

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

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

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

  @Prop({ type: [Number], default: [] })
  waveformPeaks!: number[];

  @Prop({ type: [Number], default: [] })
  waveformPeaksPreview!: number[];

  @Prop({ type: [Number], default: [] })
  waveformPeaksDetailed!: number[];

  @Prop({ type: [String], default: [] })
  imageUrls!: string[];

  @Prop({ type: [imageItemSchema], default: [] })
  imageItems!: PostImageItem[];

  @Prop({ type: [mediaVariantSetSchema], default: [] })
  imageVariants!: PostMediaVariantSet[];

  @Prop({ type: [Types.ObjectId], ref: User.name, default: [], index: true })
  taggedUserIds!: Types.ObjectId[];

  @Prop({ type: [String], default: [] })
  mentionUsernames!: string[];

  @Prop({ type: [Types.ObjectId], ref: User.name, default: [], index: true })
  mentionedUserIds!: Types.ObjectId[];

  @Prop({ type: [Types.ObjectId], ref: User.name, default: [], index: true })
  collaboratorIds!: Types.ObjectId[];

  @Prop({ default: '' })
  location!: string;

  @Prop({ type: Number, min: -90, max: 90, default: null })
  latitude!: number | null;

  @Prop({ type: Number, min: -180, max: 180, default: null })
  longitude!: number | null;

  @Prop({ enum: PostType, default: PostType.TEXT, index: true })
  postType!: PostType;

  @Prop({
    type: String,
    enum: Object.values(ProcessingStatus),
    default: ProcessingStatus.READY,
    index: true,
  })
  processingStatus!: ProcessingStatus;

  @Prop({ enum: PostVisibility, default: PostVisibility.PUBLIC, index: true })
  visibility!: PostVisibility;

  @Prop({ default: 0, min: 0 })
  likesCount!: number;

  @Prop({ default: 0, min: 0 })
  commentsCount!: number;

  @Prop({ default: 0, min: 0 })
  savesCount!: number;

  @Prop({ default: 0, min: 0 })
  shareCount!: number;

  @Prop({ default: 0, min: 0 })
  viewCount!: number;

  @Prop({ default: 0, min: 0 })
  playCount!: number;

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

  @Prop({ default: false })
  commentsFollowersOnly!: boolean;

  @Prop({ type: [String], default: [] })
  commentFilterKeywords!: string[];

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

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

  @Prop({ type: [String], default: [], index: true })
  hashtags!: string[];

  @Prop({
    type: String,
    enum: Object.values(ModerationStatus),
    default: ModerationStatus.ACTIVE,
    index: true,
  })
  moderationStatus!: ModerationStatus;

  @Prop({ default: '', maxlength: 300 })
  moderationReason!: string;

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

  @Prop({ type: Date, default: null })
  deletedAt?: Date | null;

  @Prop({ type: Types.ObjectId, ref: User.name, default: null })
  deletedBy?: Types.ObjectId | null;
}

export const PostSchema = SchemaFactory.createForClass(Post);

PostSchema.index({ authorId: 1, createdAt: -1 });
PostSchema.index({ repostOfPostId: 1, createdAt: -1 });
PostSchema.index({ quoteOfPostId: 1, createdAt: -1 });
PostSchema.index({ visibility: 1, createdAt: -1 });
PostSchema.index({ postType: 1, createdAt: -1 });
PostSchema.index({ processingStatus: 1, createdAt: -1 });
PostSchema.index({ hashtags: 1, createdAt: -1 });
PostSchema.index({ taggedUserIds: 1, createdAt: -1 });
PostSchema.index({ mentionedUserIds: 1, createdAt: -1 });
PostSchema.index({ collaboratorIds: 1, createdAt: -1 });
PostSchema.index({ authorId: 1, pinnedToProfile: -1, createdAt: -1 });
PostSchema.index({ authorId: 1, isArchived: 1, createdAt: -1 });
PostSchema.index({ moderationStatus: 1, createdAt: -1 });
PostSchema.index({ authorId: 1, isDeleted: 1, createdAt: -1 });
PostSchema.index({ visibility: 1, isDeleted: 1, createdAt: -1 });
PostSchema.index(
  {
    content: 'text',
    contentTop: 'text',
    contentBottom: 'text',
    hashtags: 'text',
    style: 'text',
    maqam: 'text',
    rhythmSignature: 'text',
  },
  { name: 'post_search_text' },
);
PostSchema.index({
  visibility: 1,
  isDeleted: 1,
  likesCount: -1,
  commentsCount: -1,
  savesCount: -1,
  shareCount: -1,
  viewCount: -1,
  playCount: -1,
  createdAt: -1,
});

const transformManagedPostFiles = (_doc: unknown, ret: any) => {
  ret.imageUrls = resolveManagedFileUrls(ret.imageUrls);
  ret.imageItems = Array.isArray(ret.imageItems)
    ? ret.imageItems.map((item: PostImageItem) => ({
        ...item,
        url: resolveManagedFileUrl(item.url),
      }))
    : [];
  ret.imageVariants = resolveManagedFileUrlRecords(ret.imageVariants);
  ret.videoUrl = resolveManagedFileUrl(ret.videoUrl);
  ret.hlsUrl = resolveManagedFileUrl(ret.hlsUrl);
  ret.audioUrl = resolveManagedFileUrl(ret.audioUrl);
  ret.thumbnailUrl = resolveManagedFileUrl(ret.thumbnailUrl);
  ret.thumbnailVariants = resolveManagedFileUrlRecord(ret.thumbnailVariants);
  const waveformSet =
    ret.postType === PostType.AUDIO || ret.audioUrl
      ? buildWaveformPeakSet(
          Array.isArray(ret.waveformPeaksDetailed) && ret.waveformPeaksDetailed.length
            ? ret.waveformPeaksDetailed
            : Array.isArray(ret.waveformPeaksPreview) && ret.waveformPeaksPreview.length
              ? ret.waveformPeaksPreview
              : ret.waveformPeaks,
          ret.durationSeconds,
        )
      : { waveformPeaks: [], waveformPeaksPreview: [], waveformPeaksDetailed: [] };
  ret.waveformPeaks = waveformSet.waveformPeaks;
  ret.waveformPeaksPreview = waveformSet.waveformPeaksPreview;
  ret.waveformPeaksDetailed = waveformSet.waveformPeaksDetailed;
  ret.mentionedUsers = Array.isArray(ret.mentionedUserIds)
    ? ret.mentionedUserIds
        .map((user: any) => {
          if (!user || typeof user !== 'object' || !('username' in user)) {
            return null;
          }
          const id = user._id?.toString?.() ?? user.id?.toString?.() ?? '';
          return {
            _id: id,
            id,
            username: user.username ?? '',
            name: user.name ?? '',
            stageName: user.stageName ?? '',
            avatar: resolveManagedFileUrl(user.avatar ?? ''),
            isVerified: user.isVerified ?? false,
          };
        })
        .filter(Boolean)
    : [];
  ret.isPinned = !!ret.pinnedToProfile;
  ret.processingStatus = ret.processingStatus ?? ProcessingStatus.READY;
  ret.media = buildPostMediaResponse(ret);
  return ret;
};

PostSchema.set('toJSON', { transform: transformManagedPostFiles });
PostSchema.set('toObject', { transform: transformManagedPostFiles });
