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

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

class ProjectsDBApi extends GenericDBApi {
  static get MODEL() {
    return db.projects;
  }

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

  static get SEARCHABLE_FIELDS() {
    return ['name', 'slug', 'description', 'logo_url', 'favicon_url', 'og_image_url', 'theme_config_json', 'custom_css_json', 'cdn_base_url', 'entry_page_slug'];
  }

  static get RANGE_FIELDS() {
    return [];
  }

  static get ENUM_FIELDS() {
    return ['phase'];
  }

  static get CSV_FIELDS() {
    return ['id', 'name', 'slug', 'description', 'phase', 'logo_url', 'cdn_base_url', 'createdAt'];
  }

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

  static get ASSOCIATIONS() {
    return [];
  }

  static getFieldMapping(data) {
    return {
      id: data.id || undefined,
      name: data.name || null,
      slug: data.slug || null,
      description: data.description || null,
      phase: data.phase || null,
      logo_url: data.logo_url || null,
      favicon_url: data.favicon_url || null,
      og_image_url: data.og_image_url || null,
      theme_config_json: data.theme_config_json || null,
      custom_css_json: data.custom_css_json || null,
      cdn_base_url: data.cdn_base_url || null,
      entry_page_slug: data.entry_page_slug || null,
    };
  }

  static get DEFAULT_INCLUDES() {
    return [];
  }

  static get ALL_INCLUDES() {
    return [
      { association: 'project_memberships_project' },
      { association: 'assets_project' },
      { association: 'presigned_url_requests_project' },
      { association: 'tour_pages_project' },
      { association: 'transitions_project' },
      { association: 'project_audio_tracks_project' },
      { association: 'publish_events_project' },
      { association: 'pwa_caches_project' },
      { association: 'access_logs_project' },
    ];
  }

  static async findBy(where, options = {}) {
    const transaction = options.transaction;
    const runtimeEnvironment = getRuntimeEnvironment(options);
    const runtimeProjectSlug = getRuntimeProjectSlug(options);
    const queryWhere = { ...where };

    if (runtimeEnvironment) {
      queryWhere.phase = runtimeEnvironment === 'production'
        ? 'production'
        : { [Op.in]: ['stage', 'production'] };
    }

    if (runtimeProjectSlug) {
      queryWhere.slug = runtimeProjectSlug;
    }

    const include = options.include !== undefined
      ? options.include
      : this.DEFAULT_INCLUDES;

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

    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 = {};
    let include = [];

    if (filter.id) {
      where.id = Utils.uuid(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 };
      }
    }

    const runtimeEnvironment = getRuntimeEnvironment(options);
    const runtimeProjectSlug = getRuntimeProjectSlug(options);

    if (runtimeEnvironment) {
      where.phase = runtimeEnvironment;
    }

    if (runtimeProjectSlug) {
      where.slug = runtimeProjectSlug;
    }

    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 = ProjectsDBApi;
