/**
 * SQL Server Dialect Implementation
 *
 * Encapsulates SQL Server-specific SQL syntax for:
 * - Table name quoting (bracket notation [schema].[table])
 * - Named parameter placeholders (@p0, @p1, ...)
 * - MERGE ... USING upsert
 * - OFFSET/FETCH pagination
 */

import type { SqlDialect } from '../../../types/sql-dialect.types'

export class SqlServerDialect implements SqlDialect {
  readonly dbType = 'sqlserver' as const

  quoteTableName(schema: string, table: string): string {
    return `[${schema}].[${table}]`
  }

  param(index: number): string {
    return `@p${index}`
  }

  params(count: number): string {
    return Array.from({ length: count }, (_, i) => `@p${i}`).join(',')
  }

  currentTimestamp(): string {
    return 'SYSUTCDATETIME()'
  }

  upsert(params: {
    table: string
    keyColumns: string[]
    allColumns: string[]
    startParamIndex: number
  }): { sql: string; nextParamIndex: number } {
    const { table, keyColumns, allColumns, startParamIndex } = params

    const valueParams = allColumns.map((_, i) => `@p${startParamIndex + i}`).join(', ')
    const sourceColumns = allColumns.join(', ')

    const joinCondition = keyColumns.map((col) => `target.${col} = source.${col}`).join(' AND ')

    const nonKeyColumns = allColumns.filter((col) => !keyColumns.includes(col))
    const updateSet = nonKeyColumns.map((col) => `target.${col} = source.${col}`).join(', ')

    const insertColumns = allColumns.join(', ')
    const insertValues = allColumns.map((col) => `source.${col}`).join(', ')

    const sql = [
      `MERGE ${table} AS target`,
      `USING (VALUES (${valueParams})) AS source (${sourceColumns})`,
      `ON ${joinCondition}`,
      `WHEN MATCHED THEN UPDATE SET ${updateSet}`,
      `WHEN NOT MATCHED THEN INSERT (${insertColumns}) VALUES (${insertValues});`
    ].join(' ')

    return {
      sql,
      nextParamIndex: startParamIndex + allColumns.length
    }
  }

  paginate(params: { sql: string; limit: number; offset?: number; paramIndex: number }): {
    sql: string
    nextParamIndex: number
  } {
    const { sql, offset, paramIndex } = params
    void params.limit // used by caller to push param values

    if (offset !== undefined) {
      return {
        sql: `${sql} OFFSET @p${paramIndex} ROWS FETCH NEXT @p${paramIndex + 1} ROWS ONLY`,
        nextParamIndex: paramIndex + 2
      }
    }

    return {
      sql: `${sql} OFFSET 0 ROWS FETCH NEXT @p${paramIndex} ROWS ONLY`,
      nextParamIndex: paramIndex + 1
    }
  }

  maxBatchRows(columnsPerRow: number): number {
    return Math.floor(2000 / columnsPerRow)
  }
}
