const couchdbService = require("../services/couchdbService");
const { 
  ValidationError,
  NotFoundError, 
  DatabaseError, 
  withErrorHandling,
  createErrorContext
} = require("../utils/modelErrors");

class Post {
  constructor(data) {
    // Handle both new documents and database documents
    const isNew = !data._id;
    
    // For new documents, validate required fields
    if (isNew) {
      if (!data.user) {
        throw new ValidationError('User is required', 'user', data.user);
      }
      // Note: Original behavior allows posts without content, so we don't validate content here
      // The validation will happen at route level if needed

      // Validate post type
      const validTypes = ['text', 'image', 'achievement'];
      if (data.type && !validTypes.includes(data.type)) {
        throw new ValidationError('Invalid post type', 'type', data.type);
      }
    }

    this._id = data._id || null;
    this._rev = data._rev || null;
    this.type = data.type || "post"; // Keep original type for database docs
    this.user = data.user;
    this.content = data.content;
    this.imageUrl = data.imageUrl || null;
    this.cloudinaryPublicId = data.cloudinaryPublicId || null;
    this.postType = data.type || 'text';
    this.likes = data.likes || [];
    this.likesCount = data.likesCount || 0;
    this.commentsCount = data.commentsCount || 0;
    this.createdAt = data.createdAt || new Date().toISOString();
    this.updatedAt = data.updatedAt || new Date().toISOString();
  }

  static async create(postData) {
    const { user, content, imageUrl, cloudinaryPublicId } = postData;
    const errorContext = createErrorContext('Post', 'create', { 
      user, 
      content: content?.substring(0, 100) + '...',
      hasImage: !!imageUrl 
    });
    
    return await withErrorHandling(async () => {
      // Validate input first before database operations
      const post = new Post(postData);
      
      // Get user data for embedding
      const userDoc = await couchdbService.findUserById(user);
      if (!userDoc) {
        throw new NotFoundError('User', user);
      }

      post._id = `post_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
      post.user = {
        userId: user,
        name: userDoc.name,
        profilePicture: userDoc.profilePicture || ""
      };

      const createdPost = await couchdbService.create(post.toJSON());

      // Update user's posts array
      userDoc.posts.push(createdPost._id);
      userDoc.stats.postsCreated = userDoc.posts.length;
      await couchdbService.update(user, userDoc);

      return createdPost;
    }, errorContext);
  }

  static async findById(postId) {
    const errorContext = createErrorContext('Post', 'findById', { postId });
    
    return await withErrorHandling(async () => {
      const doc = await couchdbService.getById(postId);
      if (doc && (doc.type === "post" || ['text', 'image', 'achievement'].includes(doc.type))) {
        return new Post(doc);
      }
      return null;
    }, errorContext);
  }

  static async find(query = {}) {
    const errorContext = createErrorContext('Post', 'find', { query });
    
    return await withErrorHandling(async () => {
      const couchQuery = {
        selector: {
          type: "post",
          ...query
        }
      };
      return await couchdbService.find(couchQuery);
    }, errorContext);
  }

  static async findAll(options = {}) {
    const { skip = 0, limit = 20, sort = { createdAt: -1 } } = options;
    const errorContext = createErrorContext('Post', 'findAll', { options });
    
    return await withErrorHandling(async () => {
      const query = {
        selector: { type: "post" },
        sort: Object.keys(sort).map(key => [key, sort[key] === -1 ? "desc" : "asc"]),
        skip,
        limit
      };

      return await couchdbService.find(query);
    }, errorContext);
  }

  static async countDocuments() {
    const errorContext = createErrorContext('Post', 'countDocuments');
    
    return await withErrorHandling(async () => {
      const query = {
        selector: { type: "post" },
        fields: ["_id"]
      };
      const docs = await couchdbService.find(query);
      return docs.length;
    }, errorContext);
  }

  static async updatePost(postId, updateData) {
    const errorContext = createErrorContext('Post', 'updatePost', { postId, updateData });
    
    return await withErrorHandling(async () => {
      const post = await couchdbService.getById(postId);
      if (!post) {
        throw new NotFoundError('Post', postId);
      }

      const updatedPost = {
        ...post,
        ...updateData,
        updatedAt: new Date().toISOString()
      };

      return await couchdbService.update(postId, updatedPost);
    }, errorContext);
  }

  static async deletePost(postId) {
    const errorContext = createErrorContext('Post', 'deletePost', { postId });
    
    return await withErrorHandling(async () => {
      const post = await couchdbService.getById(postId);
      if (!post) {
        throw new NotFoundError('Post', postId);
      }

      // Remove post from user's posts array
      if (post.user && post.user.userId) {
        const userDoc = await couchdbService.findUserById(post.user.userId);
        if (userDoc) {
          userDoc.posts = userDoc.posts.filter(id => id !== postId);
          userDoc.stats.postsCreated = userDoc.posts.length;
          await couchdbService.update(post.user.userId, userDoc);
        }
      }

      return await couchdbService.delete(postId);
    }, errorContext);
  }

  static async addLike(postId, userId) {
    const errorContext = createErrorContext('Post', 'addLike', { postId, userId });
    
    return await withErrorHandling(async () => {
      const post = await couchdbService.getById(postId);
      if (!post) {
        throw new NotFoundError('Post', postId);
      }

      if (!post.likes.includes(userId)) {
        post.likes.push(userId);
        post.likesCount = post.likes.length;
        post.updatedAt = new Date().toISOString();
        await couchdbService.update(postId, post);
      }

      return post;
    }, errorContext);
  }

  static async removeLike(postId, userId) {
    const errorContext = createErrorContext('Post', 'removeLike', { postId, userId });
    
    return await withErrorHandling(async () => {
      const post = await couchdbService.getById(postId);
      if (!post) {
        throw new NotFoundError('Post', postId);
      }

      const likeIndex = post.likes.indexOf(userId);
      if (likeIndex > -1) {
        post.likes.splice(likeIndex, 1);
        post.likesCount = post.likes.length;
        post.updatedAt = new Date().toISOString();
        await couchdbService.update(postId, post);
      }

      return post;
    }, errorContext);
  }

  static async incrementCommentsCount(postId) {
    const errorContext = createErrorContext('Post', 'incrementCommentsCount', { postId });
    
    return await withErrorHandling(async () => {
      const post = await couchdbService.getById(postId);
      if (!post) {
        throw new NotFoundError('Post', postId);
      }

      post.commentsCount = (post.commentsCount || 0) + 1;
      post.updatedAt = new Date().toISOString();
      return await couchdbService.update(postId, post);
    }, errorContext);
  }

  static async decrementCommentsCount(postId) {
    const errorContext = createErrorContext('Post', 'decrementCommentsCount', { postId });
    
    return await withErrorHandling(async () => {
      const post = await couchdbService.getById(postId);
      if (!post) {
        throw new NotFoundError('Post', postId);
      }

      post.commentsCount = Math.max(0, (post.commentsCount || 0) - 1);
      post.updatedAt = new Date().toISOString();
      return await couchdbService.update(postId, post);
    }, errorContext);
  }

  static async findByUserId(userId, options = {}) {
    const { skip = 0, limit = 20 } = options;
    const errorContext = createErrorContext('Post', 'findByUserId', { userId, options });
    
    return await withErrorHandling(async () => {
      const query = {
        selector: {
          type: "post",
          "user.userId": userId
        },
        sort: [["createdAt", "desc"]],
        skip,
        limit
      };

      return await couchdbService.find(query);
    }, errorContext);
  }

  // Convert to CouchDB document format
  toJSON() {
    return {
      _id: this._id,
      _rev: this._rev,
      type: this.type,
      user: this.user,
      content: this.content,
      imageUrl: this.imageUrl,
      cloudinaryPublicId: this.cloudinaryPublicId,
      postType: this.postType,
      likes: this.likes,
      likesCount: this.likesCount,
      commentsCount: this.commentsCount,
      createdAt: this.createdAt,
      updatedAt: this.updatedAt
    };
  }

  // Instance save method
  async save() {
    const errorContext = createErrorContext('Post', 'save', { 
      id: this._id,
      isNew: !this._id 
    });
    
    return await withErrorHandling(async () => {
      if (!this._id) {
        // New document
        this._id = `post_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
        const created = await couchdbService.createDocument(this.toJSON());
        this._rev = created._rev;
        return this;
      } else {
        // Update existing document
        this.updatedAt = new Date().toISOString();
        const updated = await couchdbService.updateDocument(this.toJSON());
        this._rev = updated._rev;
        return this;
      }
    }, errorContext);
  }

  // Legacy compatibility methods for mongoose-like interface
  static async populate(posts, fields) {
    // In CouchDB, user data is already embedded in posts
    // This method is for compatibility with existing code
    if (fields && fields.includes("user")) {
      // User data is already embedded, so just return posts as-is
      return posts;
    }
    return posts;
  }
}

module.exports = Post;