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

export const executionLogRepository = {
  async create(log) {
    const id = crypto.randomUUID()
    await db.run(
      `INSERT INTO execution_log (id, session_id, started_at, finished_at, status)
       VALUES (?, ?, ?, ?, ?)`,
      [id, log.session_id, log.started_at, log.finished_at || null, log.status || 'completed'],
    )
    return id
  },

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

  async getAll() {
    return db.selectAll('SELECT * FROM execution_log ORDER BY started_at DESC')
  },

  async getBySessionId(sessionId) {
    return db.selectAll(
      'SELECT * FROM execution_log WHERE session_id = ? ORDER BY started_at DESC',
      [sessionId],
    )
  },

  async update(id, data) {
    if (data.status !== undefined) {
      return db.run('UPDATE execution_log SET finished_at = ?, status = ? WHERE id = ?', [
        data.finished_at,
        data.status,
        id,
      ])
    }
    return db.run('UPDATE execution_log SET finished_at = ? WHERE id = ?', [data.finished_at, id])
  },

  async delete(id) {
    return db.run('DELETE FROM execution_log WHERE id = ?', [id])
  },

  async search(query) {
    return db.selectAll(
      `SELECT el.* FROM execution_log el
       JOIN session s ON s.id = el.session_id
       WHERE s.title LIKE ? ESCAPE '\\'
       ORDER BY el.started_at DESC`,
      [`%${escapeLike(query)}%`],
    )
  },

  async getItems(logId) {
    return db.selectAll(`SELECT * FROM execution_log_item WHERE log_id = ? ORDER BY timestamp`, [
      logId,
    ])
  },

  async addItem(item) {
    const id = crypto.randomUUID()
    await db.run(
      `INSERT INTO execution_log_item (id, log_id, exercise_id, combo_id, round_number, reps_done, weight_used, side, completed, timestamp)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
      [
        id,
        item.log_id,
        item.exercise_id,
        item.combo_id || null,
        item.round_number || null,
        item.reps_done || 0,
        item.weight_used || 0,
        item.side || null,
        item.completed ? 1 : 0,
        item.timestamp || new Date().toISOString(),
      ],
    )
    return id
  },

  async createWithId(id, log) {
    await db.run(
      `INSERT INTO execution_log (id, session_id, started_at, finished_at, status)
       VALUES (?, ?, ?, ?, ?)`,
      [id, log.session_id, log.started_at, log.finished_at || null, log.status || 'completed'],
    )
    return id
  },

  async addItems(logId, items) {
    if (!items.length) return
    const statements = items.map((item) => ({
      sql: `INSERT INTO execution_log_item (id, log_id, exercise_id, combo_id, round_number, reps_done, weight_used, side, completed, timestamp)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
      params: [
        item.id,
        logId,
        item.exercise_id,
        item.combo_id || null,
        item.round_number || null,
        item.reps_done || 0,
        item.weight_used || 0,
        item.side || null,
        item.completed ? 1 : 0,
        item.timestamp || new Date().toISOString(),
      ],
    }))
    await db.batch(statements)
  },

  async getLastItemsBySession(sessionId) {
    const log = await db.selectOne(
      'SELECT id FROM execution_log WHERE session_id = ? ORDER BY started_at DESC LIMIT 1',
      [sessionId],
    )
    if (!log) return {}
    const items = await db.selectAll(
      'SELECT exercise_id, reps_done, weight_used, side FROM execution_log_item WHERE log_id = ? ORDER BY timestamp',
      [log.id],
    )
    const map = {}
    for (const item of items) {
      map[item.exercise_id] = {
        reps_done: item.reps_done,
        weight_used: item.weight_used,
        side: item.side,
      }
    }
    return map
  },

  async getTotalSessionsSince(since = null) {
    if (!since) {
      const row = await db.selectOne(
        "SELECT COUNT(*) AS count FROM execution_log WHERE status = 'completed'",
      )
      return row ? row.count : 0
    }
    return this.getCountSince(since)
  },

  async getTotalExercisesDone(since = null) {
    let sql = `SELECT COUNT(*) AS count FROM execution_log_item eli
               JOIN execution_log el ON el.id = eli.log_id
               WHERE eli.completed = 1`
    const params = []
    if (since) {
      sql += ' AND el.started_at >= ?'
      params.push(since)
    }
    const row = await db.selectOne(sql, params)
    return row ? row.count : 0
  },

  async getDistinctSessionDays() {
    return db.selectAll(
      `SELECT DATE(started_at) AS day FROM execution_log WHERE status = 'completed' GROUP BY DATE(started_at) ORDER BY day DESC`,
    )
  },

  async getFrequencyData(since = null, groupBy = 'week') {
    const fmt = groupBy === 'month' ? '%Y-%m' : '%Y-%W'
    let sql = `SELECT strftime('${fmt}', started_at) AS period, COUNT(*) AS count FROM execution_log WHERE status = 'completed'`
    const params = []
    if (since) {
      sql += ' AND started_at >= ?'
      params.push(since)
    }
    sql += ' GROUP BY period ORDER BY period'
    return db.selectAll(sql, params)
  },

  async getVolumeData(since = null) {
    let sql = `SELECT el.id, DATE(el.started_at) AS day, el.started_at,
                      SUM(eli.reps_done * eli.weight_used) AS volume
               FROM execution_log el
               JOIN execution_log_item eli ON eli.log_id = el.id
               WHERE eli.completed = 1`
    const params = []
    if (since) {
      sql += ' AND el.started_at >= ?'
      params.push(since)
    }
    sql += ' GROUP BY el.id ORDER BY el.started_at'
    return db.selectAll(sql, params)
  },

  async getLoggedExercises() {
    return db.selectAll(
      `SELECT DISTINCT e.id, e.title
       FROM exercise e
       JOIN execution_log_item eli ON eli.exercise_id = e.id
       ORDER BY e.title`,
    )
  },

  async getProgressionData(exerciseId, since = null) {
    let sql = `SELECT el.started_at,
                      SUM(eli.reps_done) AS total_reps,
                      MAX(eli.weight_used) AS max_weight
               FROM execution_log el
               JOIN execution_log_item eli ON eli.log_id = el.id
               WHERE eli.exercise_id = ? AND eli.completed = 1`
    const params = [exerciseId]
    if (since) {
      sql += ' AND el.started_at >= ?'
      params.push(since)
    }
    sql += ' GROUP BY el.id ORDER BY el.started_at'
    return db.selectAll(sql, params)
  },

  async getRecent(limit = 3) {
    return db.selectAll(
      `SELECT el.*, s.title AS session_title
       FROM execution_log el
       JOIN session s ON s.id = el.session_id
       ORDER BY el.started_at DESC
       LIMIT ?`,
      [limit],
    )
  },

  async getItemsWithTitles(logId) {
    return db.selectAll(
      `SELECT eli.*, e.title AS exercise_title,
              c.title AS combo_title, c.type AS combo_type
       FROM execution_log_item eli
       JOIN exercise e ON e.id = eli.exercise_id
       LEFT JOIN combo c ON c.id = eli.combo_id
       WHERE eli.log_id = ?
       ORDER BY eli.timestamp`,
      [logId],
    )
  },

  async getRecentWithItems(limit = 4) {
    const logs = await db.selectAll(
      `SELECT el.*, s.title AS session_title
       FROM execution_log el
       JOIN session s ON s.id = el.session_id
       ORDER BY el.started_at DESC
       LIMIT ?`,
      [limit],
    )
    for (const log of logs) {
      log.items = await this.getItemsWithTitles(log.id)
    }
    return logs
  },

  async getAllWithItems() {
    const logs = await db.selectAll(
      `SELECT el.*, s.title AS session_title
       FROM execution_log el
       JOIN session s ON s.id = el.session_id
       ORDER BY el.started_at DESC`,
    )
    for (const log of logs) {
      log.items = await this.getItemsWithTitles(log.id)
    }
    return logs
  },

  async getCountSince(isoDate) {
    const row = await db.selectOne(
      `SELECT COUNT(*) AS count FROM execution_log WHERE status = 'completed' AND started_at >= ?`,
      [isoDate],
    )
    return row ? row.count : 0
  },

  async countItemsByExercise(exerciseId) {
    const row = await db.selectOne(
      `SELECT COUNT(*) AS count FROM execution_log_item WHERE exercise_id = ?`,
      [exerciseId],
    )
    return row ? row.count : 0
  },

  async countLogsBySession(sessionId) {
    const row = await db.selectOne(
      `SELECT COUNT(*) AS count FROM execution_log WHERE session_id = ?`,
      [sessionId],
    )
    return row ? row.count : 0
  },

  async getCountByDateRange(from, to) {
    const row = await db.selectOne(
      `SELECT COUNT(*) AS count FROM execution_log WHERE DATE(started_at) BETWEEN ? AND ?`,
      [from, to],
    )
    return row ? row.count : 0
  },

  async deleteByDateRange(from, to) {
    return db.run(`DELETE FROM execution_log WHERE DATE(started_at) BETWEEN ? AND ?`, [from, to])
  },
}
