#!/usr/bin/env node

require("dotenv").config();
const mongoose = require("mongoose");
const couchdbService = require("../services/couchdbService");

// MongoDB models
const User = require("../models/User");
const Street = require("../models/Street");
const Task = require("../models/Task");
const Post = require("../models/Post");
const Event = require("../models/Event");
const Report = require("../models/Report");
const Reward = require("../models/Reward");
const Badge = require("../models/Badge");
const Comment = require("../models/Comment");
const PointTransaction = require("../models/PointTransaction");
const UserBadge = require("../models/UserBadge");

class MigrationService {
  constructor() {
    this.stats = {
      users: 0,
      streets: 0,
      tasks: 0,
      posts: 0,
      events: 0,
      reports: 0,
      rewards: 0,
      badges: 0,
      comments: 0,
      pointTransactions: 0,
      userBadges: 0,
      errors: 0
    };
  }

  async initialize() {
    console.log("🚀 Starting MongoDB to CouchDB migration...");
    
    // Connect to MongoDB
    try {
      await mongoose.connect(process.env.MONGO_URI);
      console.log("✅ Connected to MongoDB");
    } catch (error) {
      console.error("❌ Failed to connect to MongoDB:", error.message);
      process.exit(1);
    }

    // Initialize CouchDB
    try {
      await couchdbService.initialize();
      console.log("✅ Connected to CouchDB");
    } catch (error) {
      console.error("❌ Failed to connect to CouchDB:", error.message);
      process.exit(1);
    }
  }

  // Transform functions for each document type
  transformUser(mongoUser) {
    return {
      _id: couchdbService.generateId("user", mongoUser._id.toString()),
      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 => 
        couchdbService.generateId("street", id.toString())
      ),
      completedTasks: (mongoUser.completedTasks || []).map(id => 
        couchdbService.generateId("task", id.toString())
      ),
      posts: (mongoUser.posts || []).map(id => 
        couchdbService.generateId("post", id.toString())
      ),
      events: (mongoUser.events || []).map(id => 
        couchdbService.generateId("event", id.toString())
      ),
      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 // Will be calculated from UserBadge collection
      },
      createdAt: mongoUser.createdAt || new Date().toISOString(),
      updatedAt: mongoUser.updatedAt || new Date().toISOString()
    };
  }

  transformStreet(mongoStreet) {
    return {
      _id: couchdbService.generateId("street", mongoStreet._id.toString()),
      type: "street",
      name: mongoStreet.name,
      location: mongoStreet.location,
      adoptedBy: mongoStreet.adoptedBy ? {
        userId: couchdbService.generateId("user", mongoStreet.adoptedBy.toString()),
        name: "", // Will be populated in relationship resolution
        profilePicture: ""
      } : null,
      status: mongoStreet.adoptedBy ? "adopted" : "available",
      stats: {
        tasksCount: 0, // Will be calculated from Task collection
        completedTasksCount: 0, // Will be calculated from Task collection
        reportsCount: 0, // Will be calculated from Report collection
        openReportsCount: 0 // Will be calculated from Report collection
      },
      createdAt: mongoStreet.createdAt || new Date().toISOString(),
      updatedAt: mongoStreet.updatedAt || new Date().toISOString()
    };
  }

  transformTask(mongoTask) {
    return {
      _id: couchdbService.generateId("task", mongoTask._id.toString()),
      type: "task",
      street: {
        streetId: couchdbService.generateId("street", mongoTask.street.toString()),
        name: "", // Will be populated in relationship resolution
        location: null // Will be populated in relationship resolution
      },
      description: mongoTask.description,
      completedBy: mongoTask.completedBy ? {
        userId: couchdbService.generateId("user", mongoTask.completedBy.toString()),
        name: "", // Will be populated in relationship resolution
        profilePicture: ""
      } : null,
      status: mongoTask.completedBy ? "completed" : "pending",
      completedAt: mongoTask.completedAt,
      pointsAwarded: mongoTask.pointsAwarded || 10,
      createdAt: mongoTask.createdAt || new Date().toISOString(),
      updatedAt: mongoTask.updatedAt || new Date().toISOString()
    };
  }

  transformPost(mongoPost) {
    return {
      _id: couchdbService.generateId("post", mongoPost._id.toString()),
      type: "post",
      user: {
        userId: couchdbService.generateId("user", mongoPost.user.toString()),
        name: "", // Will be populated in relationship resolution
        profilePicture: ""
      },
      content: mongoPost.content,
      imageUrl: mongoPost.imageUrl,
      cloudinaryPublicId: mongoPost.cloudinaryPublicId,
      likes: (mongoPost.likes || []).map(id => 
        couchdbService.generateId("user", id.toString())
      ),
      likesCount: (mongoPost.likes || []).length,
      commentsCount: 0, // Will be calculated from Comment collection
      createdAt: mongoPost.createdAt || new Date().toISOString(),
      updatedAt: mongoPost.updatedAt || new Date().toISOString()
    };
  }

  transformEvent(mongoEvent) {
    return {
      _id: couchdbService.generateId("event", mongoEvent._id.toString()),
      type: "event",
      title: mongoEvent.title,
      description: mongoEvent.description,
      date: mongoEvent.date,
      location: mongoEvent.location,
      participants: (mongoEvent.participants || []).map(userId => ({
        userId: couchdbService.generateId("user", userId.toString()),
        name: "", // Will be populated in relationship resolution
        profilePicture: "",
        joinedAt: new Date().toISOString() // Default join time
      })),
      participantsCount: (mongoEvent.participants || []).length,
      status: mongoEvent.status || "upcoming",
      createdAt: mongoEvent.createdAt || new Date().toISOString(),
      updatedAt: mongoEvent.updatedAt || new Date().toISOString()
    };
  }

  transformReport(mongoReport) {
    return {
      _id: couchdbService.generateId("report", mongoReport._id.toString()),
      type: "report",
      street: {
        streetId: couchdbService.generateId("street", mongoReport.street.toString()),
        name: "", // Will be populated in relationship resolution
        location: null // Will be populated in relationship resolution
      },
      user: {
        userId: couchdbService.generateId("user", mongoReport.user.toString()),
        name: "", // Will be populated in relationship resolution
        profilePicture: ""
      },
      issue: mongoReport.issue,
      imageUrl: mongoReport.imageUrl,
      cloudinaryPublicId: mongoReport.cloudinaryPublicId,
      status: mongoReport.status || "open",
      createdAt: mongoReport.createdAt || new Date().toISOString(),
      updatedAt: mongoReport.updatedAt || new Date().toISOString()
    };
  }

  transformBadge(mongoBadge) {
    return {
      _id: couchdbService.generateId("badge", mongoBadge._id.toString()),
      type: "badge",
      name: mongoBadge.name,
      description: mongoBadge.description,
      icon: mongoBadge.icon,
      criteria: mongoBadge.criteria,
      rarity: mongoBadge.rarity || "common",
      order: mongoBadge.order || 0,
      isActive: mongoBadge.isActive !== false,
      createdAt: mongoBadge.createdAt || new Date().toISOString(),
      updatedAt: mongoBadge.updatedAt || new Date().toISOString()
    };
  }

  transformComment(mongoComment) {
    return {
      _id: couchdbService.generateId("comment", mongoComment._id.toString()),
      type: "comment",
      post: {
        postId: couchdbService.generateId("post", mongoComment.post.toString()),
        content: "", // Will be populated in relationship resolution
        userId: "" // Will be populated in relationship resolution
      },
      user: {
        userId: couchdbService.generateId("user", mongoComment.user.toString()),
        name: "", // Will be populated in relationship resolution
        profilePicture: ""
      },
      content: mongoComment.content,
      createdAt: mongoComment.createdAt || new Date().toISOString(),
      updatedAt: mongoComment.updatedAt || new Date().toISOString()
    };
  }

  transformPointTransaction(mongoTransaction) {
    return {
      _id: couchdbService.generateId("transaction", mongoTransaction._id.toString()),
      type: "point_transaction",
      user: {
        userId: couchdbService.generateId("user", mongoTransaction.user.toString()),
        name: "" // Will be populated in relationship resolution
      },
      amount: mongoTransaction.amount,
      type: mongoTransaction.type,
      description: mongoTransaction.description,
      relatedEntity: mongoTransaction.relatedEntity ? {
        entityType: mongoTransaction.relatedEntity.entityType,
        entityId: mongoTransaction.relatedEntity.entityId ? 
          couchdbService.generateId(
            mongoTransaction.relatedEntity.entityType.toLowerCase(), 
            mongoTransaction.relatedEntity.entityId.toString()
          ) : null,
        entityName: mongoTransaction.relatedEntity.entityName
      } : null,
      balanceAfter: mongoTransaction.balanceAfter,
      createdAt: mongoTransaction.createdAt || new Date().toISOString()
    };
  }

  transformUserBadge(mongoUserBadge) {
    return {
      _id: couchdbService.generateId("userbadge", mongoUserBadge._id.toString()),
      type: "user_badge",
      userId: couchdbService.generateId("user", mongoUserBadge.user.toString()),
      badgeId: couchdbService.generateId("badge", mongoUserBadge.badge.toString()),
      progress: mongoUserBadge.progress || 100,
      earnedAt: mongoUserBadge.earnedAt || new Date().toISOString(),
      createdAt: mongoUserBadge.createdAt || new Date().toISOString(),
      updatedAt: mongoUserBadge.updatedAt || new Date().toISOString()
    };
  }

  // Migration methods
  async migrateCollection(mongoModel, transformFn, collectionName) {
    console.log(`📦 Migrating ${collectionName}...`);
    
    try {
      const documents = await mongoModel.find({});
      console.log(`Found ${documents.length} ${collectionName} documents`);
      
      const transformedDocs = documents.map(doc => transformFn(doc));
      
      // Batch insert
      if (transformedDocs.length > 0) {
        const result = await couchdbService.bulkDocs(transformedDocs);
        
        // Count successful migrations
        const successful = result.filter(r => r.ok).length;
        this.stats[collectionName] = successful;
        
        console.log(`✅ Successfully migrated ${successful}/${transformedDocs.length} ${collectionName}`);
        
        if (successful < transformedDocs.length) {
          console.log(`⚠️  ${transformedDocs.length - successful} ${collectionName} failed to migrate`);
          this.stats.errors += transformedDocs.length - successful;
        }
      }
    } catch (error) {
      console.error(`❌ Error migrating ${collectionName}:`, error.message);
      this.stats.errors++;
    }
  }

  async resolveRelationships() {
    console.log("🔗 Resolving relationships and populating embedded data...");
    
    try {
      // Get all users for lookup
      const users = await couchdbService.findByType("user");
      const userMap = new Map(users.map(u => [u._id, u]));
      
      // Get all streets for lookup
      const streets = await couchdbService.findByType("street");
      const streetMap = new Map(streets.map(s => [s._id, s]));
      
      // Get all posts for lookup
      const posts = await couchdbService.findByType("post");
      const postMap = new Map(posts.map(p => [p._id, p]));
      
      // Update streets with adopter info
      for (const street of streets) {
        if (street.adoptedBy && street.adoptedBy.userId) {
          const user = userMap.get(street.adoptedBy.userId);
          if (user) {
            street.adoptedBy.name = user.name;
            street.adoptedBy.profilePicture = user.profilePicture || "";
          }
        }
      }
      
      // Update tasks with street and user info
      const tasks = await couchdbService.findByType("task");
      for (const task of tasks) {
        if (task.street && task.street.streetId) {
          const street = streetMap.get(task.street.streetId);
          if (street) {
            task.street.name = street.name;
            task.street.location = street.location;
          }
        }
        
        if (task.completedBy && task.completedBy.userId) {
          const user = userMap.get(task.completedBy.userId);
          if (user) {
            task.completedBy.name = user.name;
            task.completedBy.profilePicture = user.profilePicture || "";
          }
        }
      }
      
      // Update posts with user info
      for (const post of posts) {
        if (post.user && post.user.userId) {
          const user = userMap.get(post.user.userId);
          if (user) {
            post.user.name = user.name;
            post.user.profilePicture = user.profilePicture || "";
          }
        }
      }
      
      // Update comments with post and user info
      const comments = await couchdbService.findByType("comment");
      for (const comment of comments) {
        if (comment.post && comment.post.postId) {
          const post = postMap.get(comment.post.postId);
          if (post) {
            comment.post.content = post.content;
            comment.post.userId = post.user.userId;
          }
        }
        
        if (comment.user && comment.user.userId) {
          const user = userMap.get(comment.user.userId);
          if (user) {
            comment.user.name = user.name;
            comment.user.profilePicture = user.profilePicture || "";
          }
        }
      }
      
      // Update events with participant info
      const events = await couchdbService.findByType("event");
      for (const event of events) {
        for (const participant of event.participants) {
          const user = userMap.get(participant.userId);
          if (user) {
            participant.name = user.name;
            participant.profilePicture = user.profilePicture || "";
          }
        }
      }
      
      // Update reports with street and user info
      const reports = await couchdbService.findByType("report");
      for (const report of reports) {
        if (report.street && report.street.streetId) {
          const street = streetMap.get(report.street.streetId);
          if (street) {
            report.street.name = street.name;
            report.street.location = street.location;
          }
        }
        
        if (report.user && report.user.userId) {
          const user = userMap.get(report.user.userId);
          if (user) {
            report.user.name = user.name;
            report.user.profilePicture = user.profilePicture || "";
          }
        }
      }
      
      // Update point transactions with user info
      const transactions = await couchdbService.findByType("point_transaction");
      for (const transaction of transactions) {
        if (transaction.user && transaction.user.userId) {
          const user = userMap.get(transaction.user.userId);
          if (user) {
            transaction.user.name = user.name;
          }
        }
      }
      
      // Update user badges and calculate stats
      const userBadges = await couchdbService.findByType("user_badge");
      const badgeMap = new Map();
      
      // Create badge lookup
      const badges = await couchdbService.findByType("badge");
      for (const badge of badges) {
        badgeMap.set(badge._id, badge);
      }
      
      // Update users with badge info
      for (const user of users) {
        const userBadgeDocs = userBadges.filter(ub => ub.userId === user._id);
        user.earnedBadges = userBadgeDocs.map(ub => {
          const badge = badgeMap.get(ub.badgeId);
          return {
            badgeId: ub.badgeId,
            name: badge ? badge.name : "Unknown Badge",
            description: badge ? badge.description : "",
            icon: badge ? badge.icon : "🏆",
            rarity: badge ? badge.rarity : "common",
            earnedAt: ub.earnedAt,
            progress: ub.progress
          };
        });
        user.stats.badgesEarned = user.earnedBadges.length;
      }
      
      // Calculate stats for streets
      for (const street of streets) {
        const streetTasks = tasks.filter(t => t.street && t.street.streetId === street._id);
        const streetReports = reports.filter(r => r.street && r.street.streetId === street._id);
        
        street.stats.tasksCount = streetTasks.length;
        street.stats.completedTasksCount = streetTasks.filter(t => t.status === "completed").length;
        street.stats.reportsCount = streetReports.length;
        street.stats.openReportsCount = streetReports.filter(r => r.status === "open").length;
      }
      
      // Calculate comment counts for posts
      for (const post of posts) {
        const postComments = comments.filter(c => c.post && c.post.postId === post._id);
        post.commentsCount = postComments.length;
      }
      
      // Batch update all documents
      const allDocs = [
        ...streets,
        ...tasks,
        ...posts,
        ...comments,
        ...events,
        ...reports,
        ...transactions,
        ...users
      ];
      
      if (allDocs.length > 0) {
        const result = await couchdbService.bulkDocs(allDocs);
        const successful = result.filter(r => r.ok).length;
        console.log(`✅ Successfully updated ${successful}/${allDocs.length} documents with relationship data`);
      }
      
    } catch (error) {
      console.error("❌ Error resolving relationships:", error.message);
      this.stats.errors++;
    }
  }

  async runMigration() {
    await this.initialize();
    
    try {
      // Phase 1: Migrate all collections
      await this.migrateCollection(User, this.transformUser.bind(this), "users");
      await this.migrateCollection(Street, this.transformStreet.bind(this), "streets");
      await this.migrateCollection(Task, this.transformTask.bind(this), "tasks");
      await this.migrateCollection(Post, this.transformPost.bind(this), "posts");
      await this.migrateCollection(Event, this.transformEvent.bind(this), "events");
      await this.migrateCollection(Report, this.transformReport.bind(this), "reports");
      await this.migrateCollection(Badge, this.transformBadge.bind(this), "badges");
      await this.migrateCollection(Comment, this.transformComment.bind(this), "comments");
      await this.migrateCollection(PointTransaction, this.transformPointTransaction.bind(this), "pointTransactions");
      await this.migrateCollection(UserBadge, this.transformUserBadge.bind(this), "userBadges");
      
      // Phase 2: Resolve relationships
      await this.resolveRelationships();
      
      // Print final statistics
      console.log("\n📊 Migration Summary:");
      console.log("====================");
      Object.entries(this.stats).forEach(([key, value]) => {
        if (key !== "errors") {
          console.log(`${key}: ${value}`);
        }
      });
      if (this.stats.errors > 0) {
        console.log(`❌ Errors: ${this.stats.errors}`);
      }
      console.log("====================");
      console.log("✅ Migration completed!");
      
    } catch (error) {
      console.error("❌ Migration failed:", error.message);
    } finally {
      await mongoose.disconnect();
      await couchdbService.shutdown();
    }
  }
}

// Run migration if this script is executed directly
if (require.main === module) {
  const migration = new MigrationService();
  migration.runMigration().catch(console.error);
}

module.exports = MigrationService;