import { db } from '../database.js'
import { escapeLike } from '../../utils/escapeLike.js'

function deserialize(row) {
  return {
    ...row,
    timer_config: JSON.parse(row.timer_config || '{}'),
  }
}

export const comboRepository = {
  async create(combo) {
    const id = crypto.randomUUID()
    await db.run(
      `INSERT INTO combo (id, title, description, type, timer_config, created_by)
       VALUES (?, ?, ?, ?, ?, ?)`,
      [
        id,
        combo.title,
        combo.description || '',
        combo.type,
        JSON.stringify(combo.timer_config || {}),
        combo.created_by,
      ],
    )
    return id
  },

  async getById(id) {
    const row = await db.selectOne('SELECT * FROM combo WHERE id = ?', [id])
    if (!row) return null
    const combo = deserialize(row)
    combo.exercises = await this.getExercises(id)
    return combo
  },

  async getAll() {
    const rows = await db.selectAll('SELECT * FROM combo ORDER BY title')
    return rows.map(deserialize)
  },

  async getAllWithExercises() {
    const combos = await this.getAll()
    for (const combo of combos) {
      combo.exercises = await this.getExercises(combo.id)
    }
    return combos
  },

  async searchWithExercises(query) {
    const combos = await this.search(query)
    for (const combo of combos) {
      combo.exercises = await this.getExercises(combo.id)
    }
    return combos
  },

  async update(id, data) {
    return db.run(
      `UPDATE combo SET title = ?, description = ?, type = ?, timer_config = ?
       WHERE id = ?`,
      [data.title, data.description || '', data.type, JSON.stringify(data.timer_config || {}), id],
    )
  },

  async delete(id) {
    // session_item.item_id is polymorphic (no FK), so combo entries in
    // sessions must be removed explicitly or they'd linger as orphans
    return db.batch([
      {
        sql: "DELETE FROM session_item WHERE item_id = ? AND item_type = 'combo'",
        params: [id],
      },
      { sql: 'DELETE FROM combo WHERE id = ?', params: [id] },
    ])
  },

  async countCombosUsingExercise(exerciseId) {
    const row = await db.selectOne(
      'SELECT COUNT(DISTINCT combo_id) AS count FROM combo_exercise WHERE exercise_id = ?',
      [exerciseId],
    )
    return row ? row.count : 0
  },

  async createWithId(id, combo) {
    await db.run(
      `INSERT INTO combo (id, title, description, type, timer_config, created_by)
       VALUES (?, ?, ?, ?, ?, ?)`,
      [
        id,
        combo.title,
        combo.description || '',
        combo.type,
        JSON.stringify(combo.timer_config || {}),
        combo.created_by,
      ],
    )
    return id
  },

  async replaceAll(id, combo) {
    await db.run(
      `UPDATE combo SET title = ?, description = ?, type = ?, timer_config = ?, created_by = ?
       WHERE id = ?`,
      [
        combo.title,
        combo.description || '',
        combo.type,
        JSON.stringify(combo.timer_config || {}),
        combo.created_by,
        id,
      ],
    )
  },

  async search(query) {
    const rows = await db.selectAll(
      "SELECT * FROM combo WHERE title LIKE ? ESCAPE '\\' ORDER BY title",
      [`%${escapeLike(query)}%`],
    )
    return rows.map(deserialize)
  },

  async getExercises(comboId) {
    const rows = await db.selectAll(
      `SELECT ce.position, ce.exercise_id, ce.default_reps, ce.default_weight,
              e.title, e.asymmetric, e.alternate
       FROM combo_exercise ce
       JOIN exercise e ON e.id = ce.exercise_id
       WHERE ce.combo_id = ?
       ORDER BY ce.position`,
      [comboId],
    )
    return rows.map((r) => ({
      ...r,
      asymmetric: Boolean(r.asymmetric),
      alternate: Boolean(r.alternate),
    }))
  },

  async getRawExercises(comboId) {
    return db.selectAll(
      `SELECT ce.position, ce.exercise_id, ce.default_reps, ce.default_weight
       FROM combo_exercise ce
       WHERE ce.combo_id = ?
       ORDER BY ce.position`,
      [comboId],
    )
  },

  async setExercises(comboId, exercises) {
    const statements = [
      { sql: 'DELETE FROM combo_exercise WHERE combo_id = ?', params: [comboId] },
      ...exercises.map((exercise, i) => {
        const exerciseId = typeof exercise === 'string' ? exercise : exercise.exerciseId
        const defaultReps = typeof exercise === 'object' ? exercise.defaultReps || 1 : 1
        const defaultWeight = typeof exercise === 'object' ? exercise.defaultWeight || 0.0 : 0.0
        return {
          sql: 'INSERT INTO combo_exercise (combo_id, exercise_id, position, default_reps, default_weight) VALUES (?, ?, ?, ?, ?)',
          params: [comboId, exerciseId, i, defaultReps, defaultWeight],
        }
      }),
    ]
    await db.batch(statements)
  },
}
