import { IMidwayApplication } from '@midwayjs/core';
import { Inject } from '@midwayjs/decorator';
import mongoose, {
  Connection,
  ConnectOptions,
  Mongoose,
  Schema,
} from 'mongoose';

export interface IMongooseApp extends IMidwayApplication {
  mongoose: Mongoose;
  mongooseDB: MongoDBConnect;
}

mongoose.set('strictQuery', false);

export class MongoDBConnect {
  @Inject()
  logger;

  private clients: Map<string, Connection>;

  private dbOptions: ConnectOptions;

  dynamicModels: { model: string; schema: Schema }[] = [];

  subDomainMap: Map<string, { dbName: string; url: string }>;

  private newConnection(
    name = 'default',
    config: { url: string; options: ConnectOptions }
  ) {
    const { url, options } = config;
    if (!url) {
      return this.logger.error('url 不能为空！');
    }
    const conn = mongoose.createConnection(url, options);
    this.clients[name] = conn;
  }

  private createClients(config: any) {
    const { client, clients } = config;
    if (client) {
      this.newConnection(client.name, client);
    }

    if (clients) {
      Object.keys(clients).forEach(dbName =>
        this.newConnection(dbName, clients[dbName])
      );
    }
  }

  private dynamicConnect(dbName: string, url: string) {
    //连接新的数据库
    const conn = mongoose.createConnection(`${url}/${dbName}`, this.dbOptions);
    this.clients[dbName] = conn;
    // 注册models
    for (const dm of this.dynamicModels) {
      conn.model(dm.model, dm.schema, dm.model);
    }
    return conn;
  }

  constructor(config: any, app: IMongooseApp) {
    this.clients = new Map();
    this.subDomainMap = new Map();
    this.createClients(config);
    this.dbOptions = config.dbOptions;
    app.mongoose = mongoose;
    app.mongooseDB = this;
  }

  get(dbName: string, url?: string) {
    const db = this.clients[dbName];
    if (db) return db;

    if (!url) throw new Error('数据库链接不能为空');
    return this.dynamicConnect(dbName, url);
  }

  async closeDB() {
    const connects: Connection[] = Object.values(this.clients);
    for (const connect of connects) {
      await connect.close();
    }
  }
}
