/**
 * SQL 쿼리 빌더 유틸리티
 * 
 * Raw Query 방식에서 안전하고 효율적인 쿼리 생성을 위한 헬퍼
 */

export interface SelectOptions {
  columns?: string[];
  where?: Record<string, any>;
  joins?: JoinClause[];
  orderBy?: string;
  limit?: number;
  offset?: number;
  groupBy?: string[];
  having?: Record<string, any>;
}

export interface JoinClause {
  type: 'INNER' | 'LEFT' | 'RIGHT' | 'FULL';
  table: string;
  on: string;
}

export interface InsertOptions {
  returning?: string[];
  onConflict?: {
    columns: string[];
    action: 'DO NOTHING' | 'DO UPDATE';
    updateSet?: string[];
  };
}

export interface UpdateOptions {
  returning?: string[];
}

export interface QueryResult {
  query: string;
  params: any[];
}

export class QueryBuilder {
  /**
   * SELECT 쿼리 생성
   */
  static select(table: string, options: SelectOptions = {}): QueryResult {
    const {
      columns = ['*'],
      where = {},
      joins = [],
      orderBy,
      limit,
      offset,
      groupBy = [],
      having = {},
    } = options;

    let query = `SELECT ${columns.join(', ')} FROM ${table}`;
    const params: any[] = [];
    let paramIndex = 1;

    // JOIN 절 추가
    for (const join of joins) {
      query += ` ${join.type} JOIN ${join.table} ON ${join.on}`;
    }

    // WHERE 절 추가
    const whereConditions = Object.keys(where);
    if (whereConditions.length > 0) {
      const whereClause = whereConditions
        .map((key) => {
          params.push(where[key]);
          return `${key} = $${paramIndex++}`;
        })
        .join(' AND ');
      query += ` WHERE ${whereClause}`;
    }

    // GROUP BY 절 추가
    if (groupBy.length > 0) {
      query += ` GROUP BY ${groupBy.join(', ')}`;
    }

    // HAVING 절 추가
    const havingConditions = Object.keys(having);
    if (havingConditions.length > 0) {
      const havingClause = havingConditions
        .map((key) => {
          params.push(having[key]);
          return `${key} = $${paramIndex++}`;
        })
        .join(' AND ');
      query += ` HAVING ${havingClause}`;
    }

    // ORDER BY 절 추가
    if (orderBy) {
      query += ` ORDER BY ${orderBy}`;
    }

    // LIMIT 절 추가
    if (limit !== undefined) {
      params.push(limit);
      query += ` LIMIT $${paramIndex++}`;
    }

    // OFFSET 절 추가
    if (offset !== undefined) {
      params.push(offset);
      query += ` OFFSET $${paramIndex++}`;
    }

    return { query, params };
  }

  /**
   * INSERT 쿼리 생성
   */
  static insert(
    table: string,
    data: Record<string, any>,
    options: InsertOptions = {}
  ): QueryResult {
    const { returning = [], onConflict } = options;

    const columns = Object.keys(data);
    const values = Object.values(data);
    const placeholders = values.map((_, index) => `$${index + 1}`).join(', ');

    let query = `INSERT INTO ${table} (${columns.join(', ')}) VALUES (${placeholders})`;

    // ON CONFLICT 절 추가
    if (onConflict) {
      query += ` ON CONFLICT (${onConflict.columns.join(', ')})`;
      
      if (onConflict.action === 'DO NOTHING') {
        query += ' DO NOTHING';
      } else if (onConflict.action === 'DO UPDATE' && onConflict.updateSet) {
        const updateSet = onConflict.updateSet
          .map(col => `${col} = EXCLUDED.${col}`)
          .join(', ');
        query += ` DO UPDATE SET ${updateSet}`;
      }
    }

    // RETURNING 절 추가
    if (returning.length > 0) {
      query += ` RETURNING ${returning.join(', ')}`;
    }

    return { query, params: values };
  }

  /**
   * UPDATE 쿼리 생성
   */
  static update(
    table: string,
    data: Record<string, any>,
    where: Record<string, any>,
    options: UpdateOptions = {}
  ): QueryResult {
    const { returning = [] } = options;

    const dataKeys = Object.keys(data);
    const dataValues = Object.values(data);
    const whereKeys = Object.keys(where);
    const whereValues = Object.values(where);

    let paramIndex = 1;

    // SET 절 생성
    const setClause = dataKeys
      .map((key) => `${key} = $${paramIndex++}`)
      .join(', ');

    // WHERE 절 생성
    const whereClause = whereKeys
      .map((key) => `${key} = $${paramIndex++}`)
      .join(' AND ');

    let query = `UPDATE ${table} SET ${setClause} WHERE ${whereClause}`;

    // RETURNING 절 추가
    if (returning.length > 0) {
      query += ` RETURNING ${returning.join(', ')}`;
    }

    const params = [...dataValues, ...whereValues];

    return { query, params };
  }

  /**
   * DELETE 쿼리 생성
   */
  static delete(table: string, where: Record<string, any>): QueryResult {
    const whereKeys = Object.keys(where);
    const whereValues = Object.values(where);

    const whereClause = whereKeys
      .map((key, index) => `${key} = $${index + 1}`)
      .join(' AND ');

    const query = `DELETE FROM ${table} WHERE ${whereClause}`;

    return { query, params: whereValues };
  }

  /**
   * COUNT 쿼리 생성
   */
  static count(table: string, where: Record<string, any> = {}): QueryResult {
    const whereKeys = Object.keys(where);
    const whereValues = Object.values(where);

    let query = `SELECT COUNT(*) as count FROM ${table}`;

    if (whereKeys.length > 0) {
      const whereClause = whereKeys
        .map((key, index) => `${key} = $${index + 1}`)
        .join(' AND ');
      query += ` WHERE ${whereClause}`;
    }

    return { query, params: whereValues };
  }

  /**
   * EXISTS 쿼리 생성
   */
  static exists(table: string, where: Record<string, any>): QueryResult {
    const whereKeys = Object.keys(where);
    const whereValues = Object.values(where);

    const whereClause = whereKeys
      .map((key, index) => `${key} = $${index + 1}`)
      .join(' AND ');

    const query = `SELECT EXISTS(SELECT 1 FROM ${table} WHERE ${whereClause}) as exists`;

    return { query, params: whereValues };
  }

  /**
   * 동적 WHERE 절 생성 (복잡한 조건)
   */
  static buildWhereClause(
    conditions: Record<string, any>,
    startParamIndex: number = 1
  ): { clause: string; params: any[]; nextParamIndex: number } {
    const keys = Object.keys(conditions);
    const params: any[] = [];
    let paramIndex = startParamIndex;

    if (keys.length === 0) {
      return { clause: '', params: [], nextParamIndex: paramIndex };
    }

    const clause = keys
      .map((key) => {
        const value = conditions[key];
        
        // 특수 연산자 처리
        if (key.includes('>>') || key.includes('->')) {
          // JSON 쿼리
          params.push(value);
          return `${key} = $${paramIndex++}`;
        } else if (Array.isArray(value)) {
          // IN 절
          const placeholders = value.map(() => `$${paramIndex++}`).join(', ');
          params.push(...value);
          return `${key} IN (${placeholders})`;
        } else if (value === null) {
          // NULL 체크
          return `${key} IS NULL`;
        } else {
          // 일반 조건
          params.push(value);
          return `${key} = $${paramIndex++}`;
        }
      })
      .join(' AND ');

    return { clause, params, nextParamIndex: paramIndex };
  }
}
