const GenericDBApi = require('./base.api');
const db = require('../models');
const Utils = require('../utils');
const {
  applyRuntimeEnvironment,
  applyRuntimeProjectFilter,
} = require('./runtime-context');

const Sequelize = db.Sequelize;
const Op = Sequelize.Op;

class Tour_pagesDBApi extends GenericDBApi {
  static get MODEL() {
    return db.tour_pages;
  }

  static get TABLE_NAME() {
    return 'tour_pages';
  }

  static get SEARCHABLE_FIELDS() {
    return [
      'source_key',
      'name',
      'slug',
      'background_image_url',
      'background_video_url',
      'background_audio_url',
      'ui_schema_json',
    ];
  }

  static get RANGE_FIELDS() {
    return ['sort_order'];
  }

  static get ENUM_FIELDS() {
    return ['environment', 'background_loop', 'requires_auth'];
  }

  static get CSV_FIELDS() {
    return [
      'id',
      'environment',
      'source_key',
      'name',
      'slug',
      'sort_order',
      'createdAt',
    ];
  }

  static get AUTOCOMPLETE_FIELD() {
    return 'name';
  }

  static get ASSOCIATIONS() {
    return [{ field: 'project', setter: 'setProject', isArray: false }];
  }

  static getFieldMapping(data) {
    return {
      id: data.id || undefined,
      environment: data.environment || null,
      source_key: data.source_key || null,
      name: data.name || null,
      slug: data.slug || null,
      sort_order: data.sort_order || null,
      background_image_url: data.background_image_url || null,
      background_video_url: data.background_video_url || null,
      background_audio_url: data.background_audio_url || null,
      background_loop: data.background_loop || false,
      requires_auth: data.requires_auth || false,
      ui_schema_json: data.ui_schema_json || null,
    };
  }

  static async create(data, options = {}) {
    const currentUser = options.currentUser || { id: null };
    const transaction = options.transaction;
    const projectId = data.project || data.projectId || null;

    const record = await this.MODEL.create(
      {
        ...this.getFieldMapping(data),
        projectId,
        importHash: data.importHash || null,
        createdById: currentUser.id,
        updatedById: currentUser.id,
      },
      { transaction },
    );

    await record.setProject(projectId, { transaction });

    return record;
  }

  static async findBy(where, options = {}) {
    const transaction = options.transaction;
    const queryWhere = applyRuntimeEnvironment({ ...where }, options);
    const projectInclude = applyRuntimeProjectFilter(
      {
        model: db.projects,
        as: 'project',
      },
      options,
    );

    const record = await this.MODEL.findOne({
      where: queryWhere,
      transaction,
      include: [projectInclude],
    });

    if (!record) return null;
    return record.get({ plain: true });
  }

  static async findAll(filter = {}, options = {}) {
    filter = filter || {};
    const limit = filter.limit || 0;
    const currentPage = +filter.page || 0;
    const offset = currentPage * limit;

    let where = {};

    const terms = filter.project ? filter.project.split('|') : [];
    const validUuids = Utils.filterValidUuids(terms);

    let include = [
      {
        model: db.projects,
        as: 'project',
        where: filter.project
          ? {
              [Op.or]: [
                ...(validUuids.length > 0
                  ? [{ id: { [Op.in]: validUuids } }]
                  : []),
                {
                  name: {
                    [Op.or]: terms.map((term) => ({ [Op.iLike]: `%${term}%` })),
                  },
                },
              ],
            }
          : {},
      },
    ];

    include[0] = applyRuntimeProjectFilter(include[0], options);

    if (filter.id) {
      if (!Utils.isValidUuid(filter.id)) {
        return { rows: [], count: 0 };
      }
      where.id = filter.id;
    }

    for (const field of this.SEARCHABLE_FIELDS) {
      if (filter[field]) {
        where[Op.and] = Utils.ilike(this.TABLE_NAME, field, filter[field]);
      }
    }

    for (const field of this.RANGE_FIELDS) {
      const rangeKey = `${field}Range`;
      if (filter[rangeKey]) {
        const [start, end] = filter[rangeKey];
        if (start !== undefined && start !== null && start !== '') {
          where[field] = { ...where[field], [Op.gte]: start };
        }
        if (end !== undefined && end !== null && end !== '') {
          where[field] = { ...where[field], [Op.lte]: end };
        }
      }
    }

    for (const field of this.ENUM_FIELDS) {
      if (filter[field] !== undefined) {
        where[field] = filter[field];
      }
    }

    if (filter.active !== undefined) {
      where.active = filter.active === true || filter.active === 'true';
    }

    if (filter.createdAtRange) {
      const [start, end] = filter.createdAtRange;
      if (start !== undefined && start !== null && start !== '') {
        where.createdAt = { ...where.createdAt, [Op.gte]: start };
      }
      if (end !== undefined && end !== null && end !== '') {
        where.createdAt = { ...where.createdAt, [Op.lte]: end };
      }
    }

    where = applyRuntimeEnvironment(where, options);

    const queryOptions = {
      where,
      include,
      distinct: true,
      order:
        filter.field && filter.sort
          ? [[filter.field, filter.sort]]
          : [['createdAt', 'desc']],
      transaction: options.transaction,
    };

    if (!options.countOnly) {
      queryOptions.limit = limit ? Number(limit) : undefined;
      queryOptions.offset = offset ? Number(offset) : undefined;
    }

    try {
      const { rows, count } = await this.MODEL.findAndCountAll(queryOptions);
      return {
        rows: options.countOnly ? [] : rows,
        count,
      };
    } catch (error) {
      console.error('Error executing query:', error);
      throw error;
    }
  }
}

module.exports = Tour_pagesDBApi;
