// Setup module path to include backend node_modules
const path = require('path');
const backendPath = path.join(__dirname, '..', 'backend');
process.env.NODE_PATH = path.join(backendPath, 'node_modules') + ':' + (process.env.NODE_PATH || '');
require('module').Module._initPaths();

const mongoose = require('mongoose');
const Nano = require('nano');

// MongoDB models (only needed for migration)
const User = require('../backend/models/User');
const Street = require('../backend/models/Street');
const Task = require('../backend/models/Task');
const Post = require('../backend/models/Post');
const Event = require('../backend/models/Event');
const Report = require('../backend/models/Report');
const Badge = require('../backend/models/Badge');
const Comment = require('../backend/models/Comment');
const PointTransaction = require('../backend/models/PointTransaction');
const UserBadge = require('../backend/models/UserBadge');

// Configuration
const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017/adopt-a-street';
const COUCHDB_URL = process.env.COUCHDB_URL || 'http://localhost:5984';
const COUCHDB_DB = process.env.COUCHDB_DB || 'adopt-a-street';

class MongoToCouchMigrator {
  constructor() {
    this.nano = Nano(COUCHDB_URL);
    this.db = null;
    this.migrationStats = {
      users: 0,
      streets: 0,
      tasks: 0,
      posts: 0,
      events: 0,
      reports: 0,
      badges: 0,
      comments: 0,
      pointTransactions: 0,
      userBadges: 0,
      errors: []
    };
  }

  async initialize() {
    console.log('🚀 Initializing migration...');
    
    // Connect to MongoDB
    await mongoose.connect(MONGO_URI);
    console.log('✅ Connected to MongoDB');

    // Initialize CouchDB
    try {
      this.db = this.nano.db.use(COUCHDB_DB);
      console.log('✅ Connected to existing CouchDB database');
    } catch (error) {
      if (error.statusCode === 404) {
        await this.nano.db.create(COUCHDB_DB);
        this.db = this.nano.db.use(COUCHDB_DB);
        console.log('✅ Created new CouchDB database');
      } else {
        throw error;
      }
    }

    // Create indexes
    await this.createIndexes();
    console.log('✅ Created CouchDB indexes');
  }

  async createIndexes() {
    const indexes = [
      {
        index: { fields: ['type', 'email'] },
        name: 'user-by-email',
        type: 'json'
      },
      {
        index: { fields: ['type', 'location'] },
        name: 'streets-by-location',
        type: 'json'
      },
      {
        index: { fields: ['type', 'user.userId'] },
        name: 'by-user',
        type: 'json'
      },
      {
        index: { fields: ['type', 'points'] },
        name: 'users-by-points',
        type: 'json'
      },
      {
        index: { fields: ['type', 'createdAt'] },
        name: 'posts-by-date',
        type: 'json'
      },
      {
        index: { fields: ['type', 'status'] },
        name: 'streets-by-status',
        type: 'json'
      },
      {
        index: { fields: ['type', 'date', 'status'] },
        name: 'events-by-date-status',
        type: 'json'
      },
      {
        index: { fields: ['type', 'post.postId'] },
        name: 'comments-by-post',
        type: 'json'
      },
      {
        index: { fields: ['type', 'user.userId', 'createdAt'] },
        name: 'transactions-by-user-date',
        type: 'json'
      }
    ];

    for (const index of indexes) {
      try {
        await this.db.createIndex(index);
      } catch (error) {
        console.warn(`⚠️  Index creation failed: ${index.name}`, error.message);
      }
    }
  }

  transformUser(mongoUser) {
    return {
      _id: `user_${mongoUser._id}`,
      type: 'user',
      name: mongoUser.name,
      email: mongoUser.email,
      password: mongoUser.password,
      isPremium: mongoUser.isPremium || false,
      points: mongoUser.points || 0,
      profilePicture: mongoUser.profilePicture,
      cloudinaryPublicId: mongoUser.cloudinaryPublicId,
      adoptedStreets: mongoUser.adoptedStreets.map(id => `street_${id}`),
      completedTasks: mongoUser.completedTasks.map(id => `task_${id}`),
      posts: mongoUser.posts.map(id => `post_${id}`),
      events: mongoUser.events.map(id => `event_${id}`),
      earnedBadges: [], // Will be populated from UserBadge collection
      stats: {
        streetsAdopted: mongoUser.adoptedStreets.length,
        tasksCompleted: mongoUser.completedTasks.length,
        postsCreated: mongoUser.posts.length,
        eventsParticipated: mongoUser.events.length,
        badgesEarned: 0
      },
      createdAt: mongoUser.createdAt,
      updatedAt: mongoUser.updatedAt
    };
  }

  transformStreet(mongoStreet) {
    return {
      _id: `street_${mongoStreet._id}`,
      type: 'street',
      name: mongoStreet.name,
      location: mongoStreet.location,
      adoptedBy: mongoStreet.adoptedBy ? {
        userId: `user_${mongoStreet.adoptedBy}`,
        name: '', // Will be populated from user data
        profilePicture: ''
      } : null,
      status: mongoStreet.status,
      stats: {
        tasksCount: 0, // Will be calculated
        completedTasksCount: 0, // Will be calculated
        reportsCount: 0, // Will be calculated
        openReportsCount: 0 // Will be calculated
      },
      createdAt: mongoStreet.createdAt,
      updatedAt: mongoStreet.updatedAt
    };
  }

  transformTask(mongoTask) {
    return {
      _id: `task_${mongoTask._id}`,
      type: 'task',
      street: {
        streetId: `street_${mongoTask.street}`,
        name: '', // Will be populated from street data
        location: null // Will be populated from street data
      },
      description: mongoTask.description,
      completedBy: mongoTask.completedBy ? {
        userId: `user_${mongoTask.completedBy}`,
        name: '', // Will be populated from user data
        profilePicture: ''
      } : null,
      status: mongoTask.status,
      completedAt: mongoTask.updatedAt, // Approximation
      pointsAwarded: 10, // Default, could be made configurable
      createdAt: mongoTask.createdAt,
      updatedAt: mongoTask.updatedAt
    };
  }

  transformPost(mongoPost) {
    return {
      _id: `post_${mongoPost._id}`,
      type: 'post',
      user: {
        userId: `user_${mongoPost.user}`,
        name: '', // Will be populated from user data
        profilePicture: ''
      },
      content: mongoPost.content,
      imageUrl: mongoPost.imageUrl,
      cloudinaryPublicId: mongoPost.cloudinaryPublicId,
      likes: mongoPost.likes.map(id => `user_${id}`),
      likesCount: mongoPost.likes.length,
      commentsCount: mongoPost.commentsCount || 0,
      createdAt: mongoPost.createdAt,
      updatedAt: mongoPost.updatedAt
    };
  }

  transformEvent(mongoEvent) {
    return {
      _id: `event_${mongoEvent._id}`,
      type: 'event',
      title: mongoEvent.title,
      description: mongoEvent.description,
      date: mongoEvent.date,
      location: mongoEvent.location,
      participants: mongoEvent.participants.map(userId => ({
        userId: `user_${userId}`,
        name: '', // Will be populated from user data
        profilePicture: '',
        joinedAt: mongoEvent.createdAt // Approximation
      })),
      participantsCount: mongoEvent.participants.length,
      status: mongoEvent.status,
      createdAt: mongoEvent.createdAt,
      updatedAt: mongoEvent.updatedAt
    };
  }

  transformReport(mongoReport) {
    return {
      _id: `report_${mongoReport._id}`,
      type: 'report',
      street: {
        streetId: `street_${mongoReport.street}`,
        name: '', // Will be populated from street data
        location: null // Will be populated from street data
      },
      user: {
        userId: `user_${mongoReport.user}`,
        name: '', // Will be populated from user data
        profilePicture: ''
      },
      issue: mongoReport.issue,
      imageUrl: mongoReport.imageUrl,
      cloudinaryPublicId: mongoReport.cloudinaryPublicId,
      status: mongoReport.status,
      createdAt: mongoReport.createdAt,
      updatedAt: mongoReport.updatedAt
    };
  }

  transformBadge(mongoBadge) {
    return {
      _id: `badge_${mongoBadge._id}`,
      type: 'badge',
      name: mongoBadge.name,
      description: mongoBadge.description,
      icon: mongoBadge.icon,
      criteria: mongoBadge.criteria,
      rarity: mongoBadge.rarity,
      order: mongoBadge.order || 0,
      isActive: true,
      createdAt: mongoBadge.createdAt,
      updatedAt: mongoBadge.updatedAt
    };
  }

  transformComment(mongoComment) {
    return {
      _id: `comment_${mongoComment._id}`,
      type: 'comment',
      post: {
        postId: `post_${mongoComment.post}`,
        content: '', // Will be populated from post data
        userId: '' // Will be populated from post data
      },
      user: {
        userId: `user_${mongoComment.user}`,
        name: '', // Will be populated from user data
        profilePicture: ''
      },
      content: mongoComment.content,
      createdAt: mongoComment.createdAt,
      updatedAt: mongoComment.updatedAt
    };
  }

  transformPointTransaction(mongoTransaction) {
    return {
      _id: `transaction_${mongoTransaction._id}`,
      type: 'point_transaction',
      user: {
        userId: `user_${mongoTransaction.user}`,
        name: '' // Will be populated from user data
      },
      amount: mongoTransaction.amount,
      type: mongoTransaction.type,
      description: mongoTransaction.description,
      relatedEntity: mongoTransaction.relatedEntity ? {
        entityType: mongoTransaction.relatedEntity.entityType,
        entityId: mongoTransaction.relatedEntity.entityId ? 
          `${mongoTransaction.relatedEntity.entityType.toLowerCase()}_${mongoTransaction.relatedEntity.entityId}` : null,
        entityName: '' // Will be populated if possible
      } : null,
      balanceAfter: mongoTransaction.balanceAfter,
      createdAt: mongoTransaction.createdAt
    };
  }

  transformUserBadge(mongoUserBadge) {
    return {
      _id: `userbadge_${mongoUserBadge._id}`,
      type: 'user_badge',
      userId: `user_${mongoUserBadge.user}`,
      badgeId: `badge_${mongoUserBadge.badge}`,
      earnedAt: mongoUserBadge.earnedAt,
      progress: mongoUserBadge.progress,
      createdAt: mongoUserBadge.createdAt,
      updatedAt: mongoUserBadge.updatedAt
    };
  }

  async migrateCollection(mongoModel, transformer, collectionName) {
    console.log(`📦 Migrating ${collectionName}...`);
    
    try {
      const documents = await mongoModel.find().lean();
      const transformedDocs = documents.map(transformer.bind(this));
      
      // Batch insert to CouchDB
      const batchSize = 100;
      for (let i = 0; i < transformedDocs.length; i += batchSize) {
        const batch = transformedDocs.slice(i, i + batchSize);
        
        for (const doc of batch) {
          try {
            await this.db.insert(doc);
            this.migrationStats[collectionName]++;
          } catch (error) {
            if (error.statusCode === 409) {
              // Document already exists, update it
              const existing = await this.db.get(doc._id);
              doc._rev = existing._rev;
              await this.db.insert(doc);
              this.migrationStats[collectionName]++;
            } else {
              console.error(`❌ Error inserting ${collectionName} ${doc._id}:`, error.message);
              this.migrationStats.errors.push(`${collectionName} ${doc._id}: ${error.message}`);
            }
          }
        }
      }
      
      console.log(`✅ Migrated ${this.migrationStats[collectionName]} ${collectionName}`);
    } catch (error) {
      console.error(`❌ Error migrating ${collectionName}:`, error.message);
      this.migrationStats.errors.push(`Collection ${collectionName}: ${error.message}`);
    }
  }

  async populateRelationships() {
    console.log('🔗 Populating relationships...');
    
    // Get all users for lookup
    const users = await this.db.find({
      selector: { type: 'user' },
      fields: ['_id', 'name', 'profilePicture']
    });
    
    const userMap = {};
    users.docs.forEach(user => {
      userMap[user._id] = {
        name: user.name,
        profilePicture: user.profilePicture || ''
      };
    });

    // Get all streets for lookup
    const streets = await this.db.find({
      selector: { type: 'street' },
      fields: ['_id', 'name', 'location']
    });
    
    const streetMap = {};
    streets.docs.forEach(street => {
      streetMap[street._id] = {
        name: street.name,
        location: street.location
      };
    });

    // Update streets with adopter info
    for (const street of streets.docs) {
      if (street.adoptedBy) {
        const adopterInfo = userMap[street.adoptedBy.userId];
        if (adopterInfo) {
          street.adoptedBy.name = adopterInfo.name;
          street.adoptedBy.profilePicture = adopterInfo.profilePicture;
          await this.db.insert(street);
        }
      }
    }

    // Update tasks with street and user info
    const tasks = await this.db.find({
      selector: { type: 'task' },
      fields: ['_id', 'street', 'completedBy']
    });

    for (const task of tasks.docs) {
      let updated = false;
      
      if (task.street && streetMap[task.street.streetId]) {
        task.street.name = streetMap[task.street.streetId].name;
        task.street.location = streetMap[task.street.streetId].location;
        updated = true;
      }
      
      if (task.completedBy && userMap[task.completedBy.userId]) {
        task.completedBy.name = userMap[task.completedBy.userId].name;
        task.completedBy.profilePicture = userMap[task.completedBy.userId].profilePicture;
        updated = true;
      }
      
      if (updated) {
        await this.db.insert(task);
      }
    }

    // Update posts with user info
    const posts = await this.db.find({
      selector: { type: 'post' },
      fields: ['_id', 'user']
    });

    for (const post of posts.docs) {
      if (userMap[post.user.userId]) {
        post.user.name = userMap[post.user.userId].name;
        post.user.profilePicture = userMap[post.user.userId].profilePicture;
        await this.db.insert(post);
      }
    }

    // Update events with participant info
    const events = await this.db.find({
      selector: { type: 'event' },
      fields: ['_id', 'participants']
    });

    for (const event of events.docs) {
      let updated = false;
      for (const participant of event.participants) {
        if (userMap[participant.userId]) {
          participant.name = userMap[participant.userId].name;
          participant.profilePicture = userMap[participant.userId].profilePicture;
          updated = true;
        }
      }
      if (updated) {
        await this.db.insert(event);
      }
    }

    // Update reports with street and user info
    const reports = await this.db.find({
      selector: { type: 'report' },
      fields: ['_id', 'street', 'user']
    });

    for (const report of reports.docs) {
      let updated = false;
      
      if (report.street && streetMap[report.street.streetId]) {
        report.street.name = streetMap[report.street.streetId].name;
        report.street.location = streetMap[report.street.streetId].location;
        updated = true;
      }
      
      if (report.user && userMap[report.user.userId]) {
        report.user.name = userMap[report.user.userId].name;
        report.user.profilePicture = userMap[report.user.userId].profilePicture;
        updated = true;
      }
      
      if (updated) {
        await this.db.insert(report);
      }
    }

    console.log('✅ Relationships populated');
  }

  async calculateStats() {
    console.log('📊 Calculating statistics...');
    
    // Calculate street stats
    const streets = await this.db.find({
      selector: { type: 'street' },
      fields: ['_id']
    });

    for (const street of streets.docs) {
      const tasks = await this.db.find({
        selector: {
          type: 'task',
          'street.streetId': street._id
        },
        fields: ['status']
      });

      const reports = await this.db.find({
        selector: {
          type: 'report',
          'street.streetId': street._id
        },
        fields: ['status']
      });

      const streetDoc = await this.db.get(street._id);
      streetDoc.stats = {
        tasksCount: tasks.docs.length,
        completedTasksCount: tasks.docs.filter(t => t.status === 'completed').length,
        reportsCount: reports.docs.length,
        openReportsCount: reports.docs.filter(r => r.status === 'open').length
      };
      
      await this.db.insert(streetDoc);
    }

    // Populate user badges
    const userBadges = await this.db.find({
      selector: { type: 'user_badge' },
      fields: ['userId', 'badgeId', 'earnedAt', 'progress']
    });

    const badges = await this.db.find({
      selector: { type: 'badge' },
      fields: ['_id', 'name', 'description', 'icon', 'rarity']
    });

    const badgeMap = {};
    badges.docs.forEach(badge => {
      badgeMap[badge._id] = {
        badgeId: badge._id,
        name: badge.name,
        description: badge.description,
        icon: badge.icon,
        rarity: badge.rarity
      };
    });

    const userBadgeMap = {};
    userBadges.docs.forEach(userBadge => {
      if (!userBadgeMap[userBadge.userId]) {
        userBadgeMap[userBadge.userId] = [];
      }
      
      const badgeInfo = badgeMap[userBadge.badgeId];
      if (badgeInfo) {
        userBadgeMap[userBadge.userId].push({
          ...badgeInfo,
          earnedAt: userBadge.earnedAt,
          progress: userBadge.progress
        });
      }
    });

    // Update users with badge info
    for (const userId in userBadgeMap) {
      const userDoc = await this.db.get(userId);
      userDoc.earnedBadges = userBadgeMap[userId];
      userDoc.stats.badgesEarned = userBadgeMap[userId].length;
      await this.db.insert(userDoc);
    }

    console.log('✅ Statistics calculated');
  }

  async runMigration() {
    try {
      await this.initialize();
      
      // Phase 1: Migrate basic documents
      await this.migrateCollection(User, this.transformUser, 'users');
      await this.migrateCollection(Street, this.transformStreet, 'streets');
      await this.migrateCollection(Task, this.transformTask, 'tasks');
      await this.migrateCollection(Post, this.transformPost, 'posts');
      await this.migrateCollection(Event, this.transformEvent, 'events');
      await this.migrateCollection(Report, this.transformReport, 'reports');
      await this.migrateCollection(Badge, this.transformBadge, 'badges');
      await this.migrateCollection(Comment, this.transformComment, 'comments');
      await this.migrateCollection(PointTransaction, this.transformPointTransaction, 'pointTransactions');
      await this.migrateCollection(UserBadge, this.transformUserBadge, 'userBadges');
      
      // Phase 2: Populate relationships
      await this.populateRelationships();
      
      // Phase 3: Calculate statistics
      await this.calculateStats();
      
      console.log('\n🎉 Migration completed!');
      console.log('\n📈 Migration Statistics:');
      console.log(`Users: ${this.migrationStats.users}`);
      console.log(`Streets: ${this.migrationStats.streets}`);
      console.log(`Tasks: ${this.migrationStats.tasks}`);
      console.log(`Posts: ${this.migrationStats.posts}`);
      console.log(`Events: ${this.migrationStats.events}`);
      console.log(`Reports: ${this.migrationStats.reports}`);
      console.log(`Badges: ${this.migrationStats.badges}`);
      console.log(`Comments: ${this.migrationStats.comments}`);
      console.log(`Point Transactions: ${this.migrationStats.pointTransactions}`);
      console.log(`User Badges: ${this.migrationStats.userBadges}`);
      
      if (this.migrationStats.errors.length > 0) {
        console.log('\n⚠️  Errors encountered:');
        this.migrationStats.errors.forEach(error => console.log(`  - ${error}`));
      }
      
    } catch (error) {
      console.error('❌ Migration failed:', error);
      throw error;
    } finally {
      await mongoose.disconnect();
      console.log('🔌 Disconnected from MongoDB');
    }
  }
}

// Run migration if called directly
if (require.main === module) {
  const migrator = new MongoToCouchMigrator();
  migrator.runMigration().catch(console.error);
}

module.exports = MongoToCouchMigrator;