import { Injectable, Logger } from '@nestjs/common';
import { DataSource } from 'typeorm';
import * as fs from 'fs';
import * as path from 'path';

@Injectable()
export class TenantProvisioningService {
  private readonly logger = new Logger(TenantProvisioningService.name);

  constructor(private readonly dataSource: DataSource) {}

  /**
   * Create a new tenant schema with all required tables.
   * Executes the tenant schema template SQL with the given tenant ID.
   */
  async provisionTenant(tenantId: string): Promise<void> {
    const schemaName = `it0_t_${tenantId}`;
    this.logger.log(`Provisioning tenant schema: ${schemaName}`);

    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();

    try {
      await queryRunner.startTransaction();

      // Read and execute the tenant schema template
      const templatePath = path.resolve(__dirname, './migrations/002-create-tenant-schema-template.sql');
      let sql = fs.readFileSync(templatePath, 'utf-8');
      sql = sql.replace(/{TENANT_ID}/g, tenantId);

      // Remove comment lines, then split by semicolon
      const cleanedSql = sql
        .split('\n')
        .filter((line) => !line.trimStart().startsWith('--'))
        .join('\n');

      const statements = cleanedSql
        .split(';')
        .map((s) => s.trim())
        .filter((s) => s.length > 0);

      for (const stmt of statements) {
        await queryRunner.query(stmt);
      }

      await queryRunner.commitTransaction();
      // Reset search_path to prevent connection pool contamination
      await queryRunner.query('RESET search_path');
      this.logger.log(`Tenant schema ${schemaName} provisioned successfully.`);
    } catch (err) {
      await queryRunner.rollbackTransaction();
      await queryRunner.query('RESET search_path').catch(() => {});
      this.logger.error(`Failed to provision tenant ${tenantId}:`, err);
      throw err;
    } finally {
      await queryRunner.release();
    }
  }

  /**
   * Drop a tenant schema (use with extreme caution).
   */
  async deprovisionTenant(tenantId: string): Promise<void> {
    const schemaName = `it0_t_${tenantId}`;
    this.logger.warn(`Deprovisioning tenant schema: ${schemaName}`);

    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();

    try {
      await queryRunner.query(`DROP SCHEMA IF EXISTS "${schemaName}" CASCADE`);
      this.logger.log(`Tenant schema ${schemaName} dropped.`);
    } finally {
      await queryRunner.release();
    }
  }

  /**
   * Check if a tenant schema exists.
   */
  async schemaExists(tenantId: string): Promise<boolean> {
    const schemaName = `it0_t_${tenantId}`;
    const result = await this.dataSource.query(
      `SELECT EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = $1)`,
      [schemaName],
    );
    return result[0]?.exists ?? false;
  }
}
