import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { DataSource } from 'typeorm';
import { AppConfig } from '../config/configuration';
import { buildTenantDataSourceOptions } from './tenant-data-source';
import { ensurePostgresSchema } from './landlord-data-source';

@Injectable()
export class TenantConnectionService implements OnModuleDestroy {
  private readonly logger = new Logger(TenantConnectionService.name);
  private readonly dataSources = new Map<string, DataSource>();
  private readonly initLocks = new Map<string, Promise<DataSource>>();

  constructor(private readonly config: ConfigService) {}

  async getTenantDataSource(subdomain: string): Promise<DataSource> {
    const existing = this.dataSources.get(subdomain);
    if (existing && existing.isInitialized) return existing;

    const inflight = this.initLocks.get(subdomain);
    if (inflight) return inflight;

    const promise = (async () => {
      const cfg = this.fullConfig();
      if (cfg.db.driver === 'postgres') {
        const schemaName = cfg.db.postgres.tenantSchemaPrefix + subdomain;
        await ensurePostgresSchema(cfg, schemaName);
      }
      const opts = buildTenantDataSourceOptions(cfg, subdomain);
      const ds = new DataSource(opts);
      await ds.initialize();
      this.dataSources.set(subdomain, ds);
      this.logger.log(`Initialized tenant DataSource for "${subdomain}"`);
      return ds;
    })();

    this.initLocks.set(subdomain, promise);
    try {
      return await promise;
    } finally {
      this.initLocks.delete(subdomain);
    }
  }

  async dropTenantConnection(subdomain: string): Promise<void> {
    const ds = this.dataSources.get(subdomain);
    if (ds && ds.isInitialized) await ds.destroy();
    this.dataSources.delete(subdomain);
  }

  async onModuleDestroy(): Promise<void> {
    for (const [name, ds] of this.dataSources) {
      if (ds.isInitialized) {
        await ds.destroy();
        this.logger.log(`Closed tenant DataSource "${name}"`);
      }
    }
    this.dataSources.clear();
  }

  private fullConfig(): AppConfig {
    return {
      port: this.config.get<number>('port')!,
      nodeEnv: this.config.get<string>('nodeEnv')!,
      jwt: {
        secret: this.config.get<string>('jwt.secret')!,
        expiresIn: this.config.get<string>('jwt.expiresIn')!,
      },
      db: {
        driver: this.config.get<AppConfig['db']['driver']>('db.driver')!,
        mysql: {
          host: this.config.get<string>('db.mysql.host')!,
          port: this.config.get<number>('db.mysql.port')!,
          user: this.config.get<string>('db.mysql.user')!,
          password: this.config.get<string>('db.mysql.password')!,
          landlordDb: this.config.get<string>('db.mysql.landlordDb')!,
          tenantDbPrefix: this.config.get<string>('db.mysql.tenantDbPrefix')!,
        },
        postgres: {
          host:               this.config.get<string>('db.postgres.host')!,
          port:               this.config.get<number>('db.postgres.port')!,
          user:               this.config.get<string>('db.postgres.user')!,
          password:           this.config.get<string>('db.postgres.password')!,
          database:           this.config.get<string>('db.postgres.database')!,
          landlordSchema:     this.config.get<string>('db.postgres.landlordSchema')!,
          tenantSchemaPrefix: this.config.get<string>('db.postgres.tenantSchemaPrefix')!,
        },
        sqlite: {
          dataDir: this.config.get<string>('db.sqlite.dataDir')!,
          landlordFile: this.config.get<string>('db.sqlite.landlordFile')!,
          tenantDir: this.config.get<string>('db.sqlite.tenantDir')!,
        },
      },
      webOrigin: this.config.get<string>('webOrigin')!,
    };
  }
}
