const db = require('../../database/database');
const { logger } = require('../../middleware/logger');

class CoffeeRepository {
  async _syncSortOrders(coffeeId, conn) {
    const cx = conn || db;
    const [rows] = await cx.query(
      `SELECT id
       FROM coffee_table_images
       WHERE coffee_id = ?
       ORDER BY sort_order ASC, id ASC`,
      [coffeeId]
    );

    for (let i = 0; i < (rows || []).length; i += 1) {
      await cx.query('UPDATE coffee_table_images SET sort_order = ? WHERE id = ?', [i, rows[i].id]);
    }
  }

  async ensureLegacyPictureRow(coffeeId, conn) {
    const cx = conn || db;

    const [coffeeRows] = await cx.query(
      `SELECT object_storage_id, original_filename
       FROM coffee_table
       WHERE id = ?
       LIMIT 1`,
      [coffeeId]
    );
    const coffee = coffeeRows?.[0];
    if (!coffee || !coffee.object_storage_id) return;

    const [countRows] = await cx.query(
      `SELECT COUNT(*) AS c
       FROM coffee_table_images
       WHERE coffee_id = ?`,
      [coffeeId]
    );
    const count = Number(countRows?.[0]?.c || 0);
    if (count > 0) return;

    await cx.query(
      `INSERT INTO coffee_table_images (coffee_id, object_storage_id, original_filename, sort_order)
       VALUES (?, ?, ?, 0)`,
      [coffeeId, coffee.object_storage_id, coffee.original_filename || null]
    );
  }

  async listPicturesByCoffeeId(coffeeId, conn) {
    const cx = conn || db;
    const [rows] = await cx.query(
      `SELECT id, coffee_id, object_storage_id, original_filename, sort_order, created_at
       FROM coffee_table_images
       WHERE coffee_id = ?
       ORDER BY sort_order ASC, id ASC`,
      [coffeeId]
    );
    return rows || [];
  }

  async deleteAllPicturesByCoffeeId(coffeeId, conn) {
    const cx = conn || db;
    const toDelete = await this.listPicturesByCoffeeId(coffeeId, cx);
    if (!toDelete.length) return [];

    await cx.query('DELETE FROM coffee_table_images WHERE coffee_id = ?', [coffeeId]);
    return toDelete;
  }

  async deletePicturesByIds(coffeeId, pictureIds, conn) {
    const cx = conn || db;
    const ids = Array.isArray(pictureIds)
      ? pictureIds.map((x) => Number(x)).filter((x) => Number.isFinite(x) && x > 0)
      : [];
    if (!ids.length) return [];

    const placeholders = ids.map(() => '?').join(',');
    const [rows] = await cx.query(
      `SELECT id, coffee_id, object_storage_id, original_filename, sort_order, created_at
       FROM coffee_table_images
       WHERE coffee_id = ?
         AND id IN (${placeholders})`,
      [coffeeId, ...ids]
    );
    if (!(rows || []).length) return [];

    const deletePlaceholders = rows.map(() => '?').join(',');
    await cx.query(
      `DELETE FROM coffee_table_images
       WHERE coffee_id = ?
         AND id IN (${deletePlaceholders})`,
      [coffeeId, ...rows.map((r) => r.id)]
    );
    return rows;
  }

  async addPictures(coffeeId, images, conn) {
    const cx = conn || db;
    const existing = await this.listPicturesByCoffeeId(coffeeId, cx);
    let nextSort = existing.length;

    for (const img of (images || [])) {
      await cx.query(
        `INSERT INTO coffee_table_images (coffee_id, object_storage_id, original_filename, sort_order)
         VALUES (?, ?, ?, ?)`,
        [
          coffeeId,
          img.object_storage_id,
          img.original_filename || null,
          nextSort,
        ]
      );
      nextSort += 1;
    }
  }

  async syncPrimaryPictureFromGallery(coffeeId, conn) {
    const cx = conn || db;
    const [rows] = await cx.query(
      `SELECT object_storage_id, original_filename
       FROM coffee_table_images
       WHERE coffee_id = ?
       ORDER BY sort_order ASC, id ASC
       LIMIT 1`,
      [coffeeId]
    );

    const first = rows?.[0];
    await cx.query(
      `UPDATE coffee_table
       SET object_storage_id = ?,
           original_filename = ?,
           updated_at = NOW()
       WHERE id = ?`,
      [first?.object_storage_id || null, first?.original_filename || null, coffeeId]
    );
  }

  async _attachPictures(rows, conn) {
    const cx = conn || db;
    if (!Array.isArray(rows) || !rows.length) return rows || [];

    const ids = rows.map((r) => Number(r.id)).filter((id) => Number.isFinite(id));
    if (!ids.length) return rows;

    const placeholders = ids.map(() => '?').join(',');
    const [pictureRows] = await cx.query(
      `SELECT id, coffee_id, object_storage_id, original_filename, sort_order, created_at
       FROM coffee_table_images
       WHERE coffee_id IN (${placeholders})
       ORDER BY coffee_id ASC, sort_order ASC, id ASC`,
      ids
    );

    const grouped = new Map();
    for (const p of (pictureRows || [])) {
      const key = Number(p.coffee_id);
      if (!grouped.has(key)) grouped.set(key, []);
      grouped.get(key).push({
        id: Number(p.id),
        coffee_id: Number(p.coffee_id),
        object_storage_id: p.object_storage_id,
        original_filename: p.original_filename,
        sort_order: Number(p.sort_order || 0),
        created_at: p.created_at,
      });
    }

    for (const row of rows) {
      const coffeeId = Number(row.id);
      const fromTable = grouped.get(coffeeId) || [];
      if (fromTable.length) {
        row.pictures = fromTable;
      } else if (row.object_storage_id) {
        row.pictures = [{
          id: null,
          coffee_id: coffeeId,
          object_storage_id: row.object_storage_id,
          original_filename: row.original_filename || null,
          sort_order: 0,
          created_at: row.created_at || null,
        }];
      } else {
        row.pictures = [];
      }
    }

    return rows;
  }

  async listAll(conn) {
    const cx = conn || db;
    const [rows] = await cx.query('SELECT * FROM coffee_table ORDER BY id DESC');
    return this._attachPictures(rows || [], cx);
  }

  async getById(id, conn) {
    const cx = conn || db;
    const [rows] = await cx.query('SELECT * FROM coffee_table WHERE id = ? LIMIT 1', [id]);
    if (!rows || !rows[0]) return null;
    const hydrated = await this._attachPictures([rows[0]], cx);
    return hydrated[0] || null;
  }

  async create(data, conn) {
    const cx = conn || db;
    const sql = `INSERT INTO coffee_table (
                   title, description, price, currency, is_featured,
                   billing_interval, interval_count,
                   object_storage_id, original_filename,
                   state, created_at, updated_at
                 ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`;
    const params = [
      data.title,
      data.description,
      data.price,
      data.currency,
      data.is_featured,
      data.billing_interval,
      data.interval_count,
      data.object_storage_id,
      data.original_filename,
      data.state
    ];
    const [result] = await cx.query(sql, params);
    const createdId = result.insertId;

    const pictures = Array.isArray(data.images) ? [...data.images] : [];
    if (!pictures.length && data.object_storage_id) {
      pictures.push({
        object_storage_id: data.object_storage_id,
        original_filename: data.original_filename || null,
        sort_order: 0,
      });
    }

    for (const picture of pictures) {
      await cx.query(
        `INSERT INTO coffee_table_images (coffee_id, object_storage_id, original_filename, sort_order)
         VALUES (?, ?, ?, ?)`,
        [
          createdId,
          picture.object_storage_id,
          picture.original_filename || null,
          Number.isFinite(Number(picture.sort_order)) ? Number(picture.sort_order) : 0,
        ]
      );
    }

    logger.info('[CoffeeRepository.create] insert', { id: result.insertId });
    return this.getById(createdId, cx);
  }

  async update(id, data, conn) {
    const cx = conn || db;
    const sql = `UPDATE coffee_table
                 SET title = ?, description = ?, price = ?, currency = ?, is_featured = ?,
                     billing_interval = ?, interval_count = ?,
                     object_storage_id = ?, original_filename = ?,
                     state = ?, updated_at = NOW()
                 WHERE id = ?`;
    const params = [
      data.title,
      data.description,
      data.price,
      data.currency,
      data.is_featured,
      data.billing_interval,
      data.interval_count,
      data.object_storage_id,
      data.original_filename,
      data.state,
      id
    ];
    const [result] = await cx.query(sql, params);
    logger.info('[CoffeeRepository.update] update', { id, affected: result.affectedRows });
    return result.affectedRows > 0;
  }

  async setState(id, state, conn) {
    const cx = conn || db;
  const [result] = await cx.query('UPDATE coffee_table SET state = ?, updated_at = NOW() WHERE id = ?', [state, id]);
    return result.affectedRows > 0;
  }

  async delete(id, conn) {
    const cx = conn || db;
  const [result] = await cx.query('DELETE FROM coffee_table WHERE id = ?', [id]);
    return result.affectedRows > 0;
  }

  async listActive(conn) {
    const cx = conn || db;
    const [rows] = await cx.query('SELECT * FROM coffee_table WHERE state = TRUE ORDER BY id DESC');
    return this._attachPictures(rows || [], cx);
  }

  async editPictures(coffeeId, { replaceAll = false, removePictureIds = [], images = [] } = {}, conn) {
    const cx = conn || db;

    await this.ensureLegacyPictureRow(coffeeId, cx);

    let deleted = [];
    if (replaceAll) {
      deleted = await this.deleteAllPicturesByCoffeeId(coffeeId, cx);
    } else if (Array.isArray(removePictureIds) && removePictureIds.length) {
      deleted = await this.deletePicturesByIds(coffeeId, removePictureIds, cx);
    }

    if (Array.isArray(images) && images.length) {
      await this.addPictures(coffeeId, images, cx);
    }

    await this._syncSortOrders(coffeeId, cx);
    await this.syncPrimaryPictureFromGallery(coffeeId, cx);

    const updated = await this.getById(coffeeId, cx);
    return {
      updated,
      deleted,
    };
  }
}

module.exports = new CoffeeRepository();
