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

class Comment {
  constructor(data) {
    // Handle both new documents and database documents
    const isNew = !data._id;
    
    // For new documents, validate required fields
    if (isNew) {
      if (!data.user || !data.user.userId) {
        throw new ValidationError('User ID is required', 'user.userId', data.user?.userId);
      }
      if (!data.post || !data.post.postId) {
        throw new ValidationError('Post ID is required', 'post.postId', data.post?.postId);
      }
      const contentStr = typeof data.content === 'string' ? data.content : String(data.content || '');
      if (!contentStr || contentStr.trim() === '') {
        throw new ValidationError('Content is required', 'content', data.content);
      }
      if (contentStr.length > 500) {
        throw new ValidationError('Content must be 500 characters or less', 'content', contentStr.length);
      }
    }

    // Assign properties
    this._id = data._id || null;
    this._rev = data._rev || null;
    this.type = data.type || "comment";
    this.user = data.user || {
      userId: null,
      name: '',
      profilePicture: ''
    };
    this.post = data.post || {
      postId: null,
      content: '',
      userId: null
    };
    const contentStr = typeof data.content === 'string' ? data.content : String(data.content || '');
    this.content = contentStr ? contentStr.trim() : '';
    this.createdAt = data.createdAt || new Date().toISOString();
    this.updatedAt = data.updatedAt || new Date().toISOString();
  }

  toJSON() {
    return {
      _id: this._id,
      _rev: this._rev,
      type: this.type,
      user: this.user,
      post: this.post,
      content: this.content,
      createdAt: this.createdAt,
      updatedAt: this.updatedAt
    };
  }

  async save() {
    const errorContext = createErrorContext('Comment', 'save', { 
      commentId: this._id,
      postId: this.post?.postId 
    });
    
    return await withErrorHandling(async () => {
      if (this._id) {
        // Update existing document
        const updatedDoc = await couchdbService.updateDocument(this._id, this.toJSON());
        Object.assign(this, updatedDoc);
        return this;
      } else {
        // Create new document
        this._id = `comment_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
        const createdDoc = await couchdbService.createDocument(this.toJSON());
        Object.assign(this, createdDoc);
        return this;
      }
    }, errorContext);
  }

  static async create(commentData) {
    const { user, post, content } = commentData;
    const errorContext = createErrorContext('Comment', 'create', { 
      userId: user,
      postId: post,
      contentLength: content?.length 
    });
    
    return await withErrorHandling(async () => {
      // Validate content first
      const validatedContent = Comment.validateContent(content);
      
      // Get user data for embedding
      const userDoc = await couchdbService.findUserById(user);
      if (!userDoc) {
        throw new NotFoundError('User', user);
      }

      // Get post data for embedding
      const postDoc = await couchdbService.getById(post);
      if (!postDoc) {
        throw new NotFoundError('Post', post);
      }

      const comment = new Comment({
        _id: `comment_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
        type: "comment",
        user: {
          userId: user,
          name: userDoc.name,
          profilePicture: userDoc.profilePicture || ""
        },
        post: {
          postId: post,
          content: postDoc.content,
          userId: postDoc.user.userId
        },
        content: validatedContent,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString()
      });

      const createdComment = await couchdbService.createDocument(comment.toJSON());

      // Update post's comment count
      const updatedPost = {
        ...postDoc,
        commentsCount: (postDoc.commentsCount || 0) + 1
      };
      await couchdbService.updateDocument(post, updatedPost);

      return new Comment(createdComment);
    }, errorContext);
  }

  static async findById(commentId) {
    const errorContext = createErrorContext('Comment', 'findById', { commentId });
    
    return await withErrorHandling(async () => {
      const doc = await couchdbService.getById(commentId);
      if (doc && doc.type === "comment") {
        return new Comment(doc);
      }
      return null;
    }, errorContext);
  }

  static async find(query = {}) {
    const errorContext = createErrorContext('Comment', 'find', { query });
    
    return await withErrorHandling(async () => {
      const couchQuery = {
        selector: {
          type: "comment",
          ...query
        }
      };
      const docs = await couchdbService.find(couchQuery);
      return docs.map(doc => new Comment(doc));
    }, errorContext);
  }

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

      const docs = await couchdbService.find(query);
      return docs.map(doc => new Comment(doc));
    }, errorContext);
  }

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

  static async deleteComment(commentId) {
    const errorContext = createErrorContext('Comment', 'deleteComment', { commentId });
    
    return await withErrorHandling(async () => {
      const comment = await this.findById(commentId);
      if (!comment) {
        throw new NotFoundError('Comment', commentId);
      }

      // Update post's comment count
      if (comment.post && comment.post.postId) {
        const postDoc = await couchdbService.getById(comment.post.postId);
        if (postDoc) {
          await couchdbService.updatePost(comment.post.postId, {
            commentsCount: Math.max(0, (postDoc.commentsCount || 0) - 1)
          });
        }
      }

      return await couchdbService.deleteDocument(commentId);
    }, errorContext);
  }

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

      const docs = await couchdbService.find(query);
      return docs.map(doc => new Comment(doc));
    }, errorContext);
  }

  static async validateContent(content) {
    if (!content || content.trim().length === 0) {
      throw new ValidationError("Comment content is required", 'content', content);
    }

    if (content.length > 500) {
      throw new ValidationError("Comment content must be 500 characters or less", 'content', content.length);
    }

    return content.trim();
  }

  // Legacy compatibility methods for mongoose-like interface
  static async populate(comments, fields) {
    const errorContext = createErrorContext('Comment', 'populate', { 
      commentCount: comments.length, 
      fields 
    });
    
    return await withErrorHandling(async () => {
      // In CouchDB, user and post data are already embedded in comments
      // This method is for compatibility with existing code
      if (fields) {
        if (fields.includes("user") || fields.includes("post")) {
          // Data is already embedded, so just return comments as-is
          return comments;
        }
      }
      return comments;
    }, errorContext);
  }

  // Helper method to check if comment belongs to a post
  static async belongsToPost(commentId, postId) {
    const errorContext = createErrorContext('Comment', 'belongsToPost', { commentId, postId });
    
    return await withErrorHandling(async () => {
      const comment = await this.findById(commentId);
      return comment && comment.post && comment.post.postId === postId;
    }, errorContext);
  }

  // Helper method to check if user owns comment
  static async isOwnedByUser(commentId, userId) {
    const errorContext = createErrorContext('Comment', 'isOwnedByUser', { commentId, userId });
    
    return await withErrorHandling(async () => {
      const comment = await this.findById(commentId);
      return comment && comment.user && comment.user.userId === userId;
    }, errorContext);
  }
}

module.exports = Comment;