import * as plugins from '../plugins.js';
import type * as interfaces from '../interfaces/index.js';
import type { TsView } from '../tsview.classes.tsview.js';
import type { ChangeStreamManager } from '../streaming/index.js';

/**
 * Register MongoDB API handlers
 */
export async function registerMongoHandlers(
  typedrouter: plugins.typedrequest.TypedRouter,
  tsview: TsView,
  changeStreamManager?: ChangeStreamManager
): Promise<void> {
  // Helper to get the native MongoDB client
  const getMongoClient = async () => {
    const db = await tsview.getMongoDb();
    if (!db) {
      throw new Error('MongoDB not configured');
    }
    // Access the underlying MongoDB client through smartdata
    return (db as any).mongoDbClient;
  };

  // Helper to create ObjectId filter
  const createIdFilter = (documentId: string) => {
    const { ObjectId } = plugins.mongodb;
    if (ObjectId.isValid(documentId)) {
      return { _id: new ObjectId(documentId) };
    }
    return { _id: documentId };
  };

  // List databases
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_ListDatabases>(
      'listDatabases',
      async () => {
        try {
          const client = await getMongoClient();
          const adminDb = client.db().admin();
          const result = await adminDb.listDatabases();

          const databases: interfaces.IMongoDatabase[] = result.databases.map((db: any) => ({
            name: db.name,
            sizeOnDisk: db.sizeOnDisk,
            empty: db.empty,
          }));

          return { databases };
        } catch (err) {
          console.error('Error listing databases:', err);
          return { databases: [] };
        }
      }
    )
  );

  // Get database stats
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_GetDatabaseStats>(
      'getDatabaseStats',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          const stats = await db.stats();
          const collections = await db.listCollections().toArray();

          return {
            stats: {
              collections: collections.length,
              views: stats.views || 0,
              objects: stats.objects || 0,
              avgObjSize: stats.avgObjSize || 0,
              dataSize: stats.dataSize || 0,
              storageSize: stats.storageSize || 0,
              indexes: stats.indexes || 0,
              indexSize: stats.indexSize || 0,
            },
          };
        } catch (err) {
          console.error('Error getting database stats:', err);
          return { stats: null };
        }
      }
    )
  );

  // List collections
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_ListCollections>(
      'listCollections',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          const collectionsInfo = await db.listCollections().toArray();

          const collections: interfaces.IMongoCollection[] = [];
          for (const coll of collectionsInfo) {
            const stats = await db.collection(coll.name).estimatedDocumentCount();
            collections.push({
              name: coll.name,
              count: stats,
            });
          }

          return { collections };
        } catch (err) {
          console.error('Error listing collections:', err);
          return { collections: [] };
        }
      }
    )
  );

  // Create database (by creating a placeholder collection)
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_CreateDatabase>(
      'createDatabase',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          // Create a placeholder collection to materialize the database
          await db.createCollection('_tsview_init');
          if (changeStreamManager) {
            changeStreamManager.emitMongoActivityEvent({
              type: 'insert',
              database: reqData.databaseName,
              collection: '_tsview_init',
              timestamp: new Date().toISOString(),
            });
          }
          return { success: true };
        } catch (err) {
          console.error('Error creating database:', err);
          return { success: false };
        }
      }
    )
  );

  // Drop database
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_DropDatabase>(
      'dropDatabase',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          await db.dropDatabase();
          if (changeStreamManager) {
            changeStreamManager.emitMongoActivityEvent({
              type: 'drop',
              database: reqData.databaseName,
              collection: '*',
              timestamp: new Date().toISOString(),
            });
          }
          return { success: true };
        } catch (err) {
          console.error('Error dropping database:', err);
          return { success: false };
        }
      }
    )
  );

  // Create collection
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_CreateCollection>(
      'createCollection',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          await db.createCollection(reqData.collectionName);
          if (changeStreamManager) {
            changeStreamManager.emitMongoActivityEvent({
              type: 'insert',
              database: reqData.databaseName,
              collection: reqData.collectionName,
              timestamp: new Date().toISOString(),
            });
          }
          return { success: true };
        } catch (err) {
          console.error('Error creating collection:', err);
          return { success: false };
        }
      }
    )
  );

  // Drop collection
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_DropCollection>(
      'dropCollection',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          await db.dropCollection(reqData.collectionName);
          if (changeStreamManager) {
            changeStreamManager.emitMongoActivityEvent({
              type: 'drop',
              database: reqData.databaseName,
              collection: reqData.collectionName,
              timestamp: new Date().toISOString(),
            });
          }
          return { success: true };
        } catch (err) {
          console.error('Error dropping collection:', err);
          return { success: false };
        }
      }
    )
  );

  // Find documents
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_FindDocuments>(
      'findDocuments',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          const collection = db.collection(reqData.collectionName);

          const filter = reqData.filter || {};
          const projection = reqData.projection || {};
          const sort = reqData.sort || {};
          const skip = reqData.skip || 0;
          const limit = reqData.limit || 50;

          const [documents, total] = await Promise.all([
            collection
              .find(filter)
              .project(projection)
              .sort(sort)
              .skip(skip)
              .limit(limit)
              .toArray(),
            collection.countDocuments(filter),
          ]);

          // Convert ObjectId to string for JSON serialization
          const serializedDocs = documents.map((doc: any) => {
            if (doc._id) {
              doc._id = doc._id.toString();
            }
            return doc;
          });

          return { documents: serializedDocs, total };
        } catch (err) {
          console.error('Error finding documents:', err);
          return { documents: [], total: 0 };
        }
      }
    )
  );

  // Get single document
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_GetDocument>(
      'getDocument',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          const collection = db.collection(reqData.collectionName);

          const filter = createIdFilter(reqData.documentId);
          const document = await collection.findOne(filter);

          if (document && document._id) {
            document._id = document._id.toString();
          }

          return { document };
        } catch (err) {
          console.error('Error getting document:', err);
          return { document: null };
        }
      }
    )
  );

  // Insert document
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_InsertDocument>(
      'insertDocument',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          const collection = db.collection(reqData.collectionName);

          const result = await collection.insertOne(reqData.document);

          if (changeStreamManager) {
            changeStreamManager.emitMongoActivityEvent({
              type: 'insert',
              database: reqData.databaseName,
              collection: reqData.collectionName,
              documentId: result.insertedId.toString(),
              timestamp: new Date().toISOString(),
            });
          }

          return { insertedId: result.insertedId.toString() };
        } catch (err) {
          console.error('Error inserting document:', err);
          throw err;
        }
      }
    )
  );

  // Update document
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_UpdateDocument>(
      'updateDocument',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          const collection = db.collection(reqData.collectionName);

          const filter = createIdFilter(reqData.documentId);

          // Check if update has $ operators
          const hasOperators = Object.keys(reqData.update).some(k => k.startsWith('$'));
          const updateDoc = hasOperators ? reqData.update : { $set: reqData.update };

          const result = await collection.updateOne(filter, updateDoc);

          if (changeStreamManager && (result.modifiedCount > 0 || result.matchedCount > 0)) {
            changeStreamManager.emitMongoActivityEvent({
              type: 'update',
              database: reqData.databaseName,
              collection: reqData.collectionName,
              documentId: reqData.documentId,
              timestamp: new Date().toISOString(),
            });
          }

          return {
            success: result.modifiedCount > 0 || result.matchedCount > 0,
            modifiedCount: result.modifiedCount,
          };
        } catch (err) {
          console.error('Error updating document:', err);
          return { success: false, modifiedCount: 0 };
        }
      }
    )
  );

  // Delete document
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_DeleteDocument>(
      'deleteDocument',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          const collection = db.collection(reqData.collectionName);

          const filter = createIdFilter(reqData.documentId);
          const result = await collection.deleteOne(filter);

          if (changeStreamManager && result.deletedCount > 0) {
            changeStreamManager.emitMongoActivityEvent({
              type: 'delete',
              database: reqData.databaseName,
              collection: reqData.collectionName,
              documentId: reqData.documentId,
              timestamp: new Date().toISOString(),
            });
          }

          return {
            success: result.deletedCount > 0,
            deletedCount: result.deletedCount,
          };
        } catch (err) {
          console.error('Error deleting document:', err);
          return { success: false, deletedCount: 0 };
        }
      }
    )
  );

  // Run aggregation pipeline
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_RunAggregation>(
      'runAggregation',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          const collection = db.collection(reqData.collectionName);

          const results = await collection.aggregate(reqData.pipeline).toArray();

          // Convert ObjectIds to strings
          const serializedResults = results.map((doc: any) => {
            if (doc._id) {
              doc._id = doc._id.toString();
            }
            return doc;
          });

          return { results: serializedResults };
        } catch (err) {
          console.error('Error running aggregation:', err);
          return { results: [] };
        }
      }
    )
  );

  // List indexes
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_ListIndexes>(
      'listIndexes',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          const collection = db.collection(reqData.collectionName);

          const indexesInfo = await collection.indexes();

          const indexes: interfaces.IMongoIndex[] = indexesInfo.map((idx: any) => ({
            name: idx.name,
            keys: idx.key,
            unique: idx.unique || false,
            sparse: idx.sparse || false,
          }));

          return { indexes };
        } catch (err) {
          console.error('Error listing indexes:', err);
          return { indexes: [] };
        }
      }
    )
  );

  // Create index
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_CreateIndex>(
      'createIndex',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          const collection = db.collection(reqData.collectionName);

          const indexName = await collection.createIndex(reqData.keys, reqData.options || {});

          return { indexName };
        } catch (err) {
          console.error('Error creating index:', err);
          throw err;
        }
      }
    )
  );

  // Drop index
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_DropIndex>(
      'dropIndex',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          const collection = db.collection(reqData.collectionName);

          await collection.dropIndex(reqData.indexName);

          return { success: true };
        } catch (err) {
          console.error('Error dropping index:', err);
          return { success: false };
        }
      }
    )
  );

  // Get collection stats
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_GetCollectionStats>(
      'getCollectionStats',
      async (reqData) => {
        try {
          const client = await getMongoClient();
          const db = client.db(reqData.databaseName);
          const collection = db.collection(reqData.collectionName);

          const stats = await db.command({ collStats: reqData.collectionName });
          const indexCount = (await collection.indexes()).length;

          return {
            stats: {
              count: stats.count || 0,
              size: stats.size || 0,
              avgObjSize: stats.avgObjSize || 0,
              storageSize: stats.storageSize || 0,
              indexCount,
            },
          };
        } catch (err) {
          console.error('Error getting collection stats:', err);
          return {
            stats: {
              count: 0,
              size: 0,
              avgObjSize: 0,
              storageSize: 0,
              indexCount: 0,
            },
          };
        }
      }
    )
  );

  // Get server status
  typedrouter.addTypedHandler(
    new plugins.typedrequest.TypedHandler<interfaces.IReq_GetServerStatus>(
      'getServerStatus',
      async () => {
        try {
          const client = await getMongoClient();
          const adminDb = client.db().admin();
          const serverInfo = await adminDb.serverInfo();
          const serverStatus = await adminDb.serverStatus();

          return {
            version: serverInfo.version || 'unknown',
            uptime: serverStatus.uptime || 0,
            connections: {
              current: serverStatus.connections?.current || 0,
              available: serverStatus.connections?.available || 0,
            },
          };
        } catch (err) {
          console.error('Error getting server status:', err);
          return {
            version: 'unknown',
            uptime: 0,
            connections: { current: 0, available: 0 },
          };
        }
      }
    )
  );
}
