import { Sequelize } from 'sequelize';

import { getDbConfig, parseDatabaseUrl } from './db-config';

export interface DatabaseConfig {
  host?: string;
  port?: number;
  database?: string;
  username?: string;
  password?: string;
  dialect?: 'postgres';
  pool?: {
    max?: number;
    min?: number;
    acquire?: number;
    idle?: number;
  };
  logging?: boolean | ((sql: string) => void);
  databaseUrl?: string; // Optional: if provided, will override other config
}

interface SequelizeOptions {
  host: string;
  port: number;
  database: string;
  username: string;
  password: string;
  dialect?: 'postgres';
  pool?: {
    max?: number;
    min?: number;
    acquire?: number;
    idle?: number;
  };
  logging?: boolean | ((sql: string) => void);
}

const defaultConfig: Partial<SequelizeOptions> = {
  dialect: 'postgres',
  pool: {
    max: 5,
    min: 0,
    acquire: 30000,
    idle: 10000,
  },
  logging: false,
};

export class Database {
  private sequelize: Sequelize;

  constructor(config: DatabaseConfig = {}) {
    // Priority: config.databaseUrl > config params > DATABASE_URL env var > individual env vars
    let connectionConfig: SequelizeOptions;

    if (config.databaseUrl) {
      // Use provided databaseUrl from config
      const parsed = parseDatabaseUrl(config.databaseUrl);
      connectionConfig = {
        ...defaultConfig,
        ...config,
        host: parsed.host,
        port: parsed.port,
        database: parsed.database,
        username: parsed.username,
        password: parsed.password,
      };
    } else if (
      config.host &&
      config.port &&
      config.database &&
      config.username &&
      config.password !== undefined
    ) {
      // Use provided config parameters
      connectionConfig = {
        ...defaultConfig,
        ...config,
        host: config.host,
        port: config.port,
        database: config.database,
        username: config.username,
        password: config.password,
      };
    } else {
      // Fall back to environment variables (DATABASE_URL or individual vars)
      const envConfig = getDbConfig();
      connectionConfig = {
        ...defaultConfig,
        ...config,
        host: config.host || envConfig.host,
        port: config.port || envConfig.port,
        database: config.database || envConfig.database,
        username: config.username || envConfig.username,
        password: config.password !== undefined ? config.password : envConfig.password,
      };
    }

    this.sequelize = new Sequelize(connectionConfig);
  }

  public getSequelize(): Sequelize {
    return this.sequelize;
  }

  public async connect(): Promise<void> {
    try {
      await this.sequelize.authenticate();
      console.log('Database connection has been established successfully.');
    } catch (error) {
      console.error('Unable to connect to the database:', error);
      throw error;
    }
  }

  public async disconnect(): Promise<void> {
    try {
      await this.sequelize.close();
      console.log('Database connection has been closed.');
    } catch (error) {
      console.error('Error closing database connection:', error);
      throw error;
    }
  }

  public async sync(options?: { force?: boolean; alter?: boolean }): Promise<void> {
    try {
      await this.sequelize.sync(options);
      console.log('Database models synchronized successfully.');
    } catch (error) {
      console.error('Error synchronizing database models:', error);
      throw error;
    }
  }
}

export default Database;
