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

class Report {
  constructor(data) {
    // Handle both new documents and database documents
    const isNew = !data._id;
    
    // For new documents, validate required fields
    if (isNew) {
      if (!data.street) {
        throw new ValidationError('Street is required', 'street', data.street);
      }
      if (!data.reporter) {
        throw new ValidationError('Reporter is required', 'reporter', data.reporter);
      }
      if (!data.type) {
        throw new ValidationError('Type is required', 'type', data.type);
      }
      if (!data.description) {
        throw new ValidationError('Description is required', 'description', data.description);
      }

      // Validate report type
      const validTypes = ['pothole', 'graffiti', 'trash', 'broken_light', 'other'];
      if (!validTypes.includes(data.type)) {
        throw new ValidationError('Invalid report type', 'type', data.type);
      }
    }

    this._id = data._id || null;
    this._rev = data._rev || null;
    this.type = data.type || "report"; // Keep original type for database docs
    this.street = data.street;
    this.reporter = data.reporter;
    this.reportType = data.type;
    this.description = data.description;
    this.status = data.status || "open";
    this.imageUrl = data.imageUrl || null;
    this.cloudinaryPublicId = data.cloudinaryPublicId || null;
    this.location = data.location || null;
    this.createdAt = data.createdAt || new Date().toISOString();
    this.updatedAt = data.updatedAt || new Date().toISOString();
  }

  static async create(reportData) {
    const errorContext = createErrorContext('Report', 'create', { 
      reportData: { ...reportData, description: reportData.description?.substring(0, 100) + '...' }
    });
    
    return await withErrorHandling(async () => {
      const report = new Report(reportData);
      return await couchdbService.createDocument(report.toJSON());
    }, errorContext);
  }

  static async findById(id) {
    const errorContext = createErrorContext('Report', 'findById', { id });
    
    return await withErrorHandling(async () => {
      const doc = await couchdbService.getDocument(id);
      if (doc && (doc.type === "report" || ['pothole', 'graffiti', 'trash', 'broken_light', 'other'].includes(doc.type))) {
        return new Report(doc);
      }
      return null;
    }, errorContext);
  }

  static async find(filter = {}) {
    const errorContext = createErrorContext('Report', 'find', { filter });
    
    return await withErrorHandling(async () => {
      const selector = {
        type: "report",
        ...filter,
      };

      return await couchdbService.findDocuments(selector);
    }, errorContext);
  }

  static async findWithPagination(options = {}) {
    const { page = 1, limit = 10, sort = { createdAt: -1 } } = options;
    const errorContext = createErrorContext('Report', 'findWithPagination', { options });
    
    return await withErrorHandling(async () => {
      const selector = { type: "report" };

      return await couchdbService.findWithPagination(selector, {
        page,
        limit,
        sort,
      });
    }, errorContext);
  }

  static async update(id, updateData) {
    const errorContext = createErrorContext('Report', 'update', { id, updateData });
    
    return await withErrorHandling(async () => {
      const doc = await couchdbService.getDocument(id);
      if (!doc || doc.type !== "report") {
        throw new NotFoundError('Report', id);
      }

      const updatedDoc = {
        ...doc,
        ...updateData,
        updatedAt: new Date().toISOString(),
      };

      return await couchdbService.updateDocument(id, updatedDoc);
    }, errorContext);
  }

  static async delete(id) {
    const errorContext = createErrorContext('Report', 'delete', { id });
    
    return await withErrorHandling(async () => {
      const doc = await couchdbService.getDocument(id);
      if (!doc || doc.type !== "report") {
        throw new NotFoundError('Report', id);
      }

      return await couchdbService.deleteDocument(id, doc._rev);
    }, errorContext);
  }

  static async countDocuments(filter = {}) {
    const errorContext = createErrorContext('Report', 'countDocuments', { filter });
    
    return await withErrorHandling(async () => {
      const selector = {
        type: "report",
        ...filter,
      };

      return await couchdbService.countDocuments(selector);
    }, errorContext);
  }

  static async findByStreet(streetId) {
    const errorContext = createErrorContext('Report', 'findByStreet', { streetId });
    
    return await withErrorHandling(async () => {
      const selector = {
        type: "report",
        "street._id": streetId,
      };

      return await couchdbService.findDocuments(selector);
    }, errorContext);
  }

  static async findByUser(userId) {
    const errorContext = createErrorContext('Report', 'findByUser', { userId });
    
    return await withErrorHandling(async () => {
      const selector = {
        type: "report",
        "user._id": userId,
      };

      return await couchdbService.findDocuments(selector);
    }, errorContext);
  }

  static async findByStatus(status) {
    const errorContext = createErrorContext('Report', 'findByStatus', { status });
    
    return await withErrorHandling(async () => {
      const selector = {
        type: "report",
        status,
      };

      return await couchdbService.findDocuments(selector);
    }, errorContext);
  }

  static async update(id, updateData) {
    const errorContext = createErrorContext('Report', 'update', { id, updateData });
    
    return await withErrorHandling(async () => {
      return await couchdbService.update(id, updateData);
    }, errorContext);
  }

  // Convert to CouchDB document format
  toJSON() {
    return {
      _id: this._id,
      _rev: this._rev,
      type: this.type,
      street: this.street,
      reporter: this.reporter,
      type: this.reportType,
      description: this.description,
      status: this.status,
      imageUrl: this.imageUrl,
      cloudinaryPublicId: this.cloudinaryPublicId,
      location: this.location,
      createdAt: this.createdAt,
      updatedAt: this.updatedAt
    };
  }

  // Instance save method
  async save() {
    const errorContext = createErrorContext('Report', 'save', { 
      id: this._id,
      isNew: !this._id 
    });
    
    return await withErrorHandling(async () => {
      if (!this._id) {
        // New document
        this._id = `report_${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);
  }
}

module.exports = Report;
