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 Page_linksDBApi extends GenericDBApi {
  static get MODEL() {
    return db.page_links;
  }

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

  static get SEARCHABLE_FIELDS() {
    return ['external_url', 'trigger_selector'];
  }

  static get RANGE_FIELDS() {
    return [];
  }

  static get ENUM_FIELDS() {
    return ['direction', 'is_active'];
  }

  static get CSV_FIELDS() {
    return ['id', 'direction', 'external_url', 'is_active', 'trigger_selector', 'createdAt'];
  }

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

  static get ASSOCIATIONS() {
    return [
      { field: 'from_page', setter: 'setFrom_page', isArray: false },
      { field: 'to_page', setter: 'setTo_page', isArray: false },
      { field: 'transition', setter: 'setTransition', isArray: false },
    ];
  }

  static getFieldMapping(data) {
    return {
      id: data.id || undefined,
      direction: data.direction || null,
      external_url: data.external_url || null,
      is_active: data.is_active || false,
      trigger_selector: data.trigger_selector || null,
    };
  }

  static async findBy(where, options = {}) {
    const transaction = options.transaction;
    const runtimeEnvironment = getRuntimeEnvironment(options);
    const runtimeProjectSlug = getRuntimeProjectSlug(options);
    const buildProjectInclude = () => (
      runtimeProjectSlug
        ? [{
            model: db.projects,
            as: 'project',
            required: true,
            where: { slug: runtimeProjectSlug },
          }]
        : []
    );

    const record = await this.MODEL.findOne({
      where,
      transaction,
      include: [
        {
          model: db.tour_pages,
          as: 'from_page',
          required: Boolean(runtimeEnvironment || runtimeProjectSlug),
          where: runtimeEnvironment ? { environment: runtimeEnvironment } : {},
          include: buildProjectInclude(),
        },
        {
          model: db.tour_pages,
          as: 'to_page',
          required: false,
          where: runtimeEnvironment ? { environment: runtimeEnvironment } : {},
          include: buildProjectInclude(),
        },
        {
          model: db.transitions,
          as: 'transition',
          required: false,
          where: runtimeEnvironment ? { environment: runtimeEnvironment } : {},
          include: buildProjectInclude(),
        },
      ],
    });

    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 = [
      {
        model: db.tour_pages,
        as: 'from_page',
        where: filter.from_page ? {
          [Op.or]: [
            { id: { [Op.in]: filter.from_page.split('|').map(term => Utils.uuid(term)) } },
            {
              name: {
                [Op.or]: filter.from_page.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
              }
            },
          ]
        } : {},
      },
      {
        model: db.tour_pages,
        as: 'to_page',
        where: filter.to_page ? {
          [Op.or]: [
            { id: { [Op.in]: filter.to_page.split('|').map(term => Utils.uuid(term)) } },
            {
              name: {
                [Op.or]: filter.to_page.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
              }
            },
          ]
        } : {},
      },
      {
        model: db.transitions,
        as: 'transition',
        where: filter.transition ? {
          [Op.or]: [
            { id: { [Op.in]: filter.transition.split('|').map(term => Utils.uuid(term)) } },
            {
              name: {
                [Op.or]: filter.transition.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
              }
            },
          ]
        } : {},
      },
    ];

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

    if (runtimeEnvironment) {
      include[0].where = { ...(include[0].where || {}), environment: runtimeEnvironment };
      include[0].required = true;
      include[1].where = { ...(include[1].where || {}), environment: runtimeEnvironment };
      include[2].where = { ...(include[2].where || {}), environment: runtimeEnvironment };
      include[2].required = false;
    }

    if (runtimeProjectSlug) {
      const projectInclude = [{
        model: db.projects,
        as: 'project',
        required: true,
        where: { slug: runtimeProjectSlug },
      }];
      include[0].include = projectInclude;
      include[0].required = true;
      include[1].include = projectInclude;
      include[2].include = projectInclude;
      include[2].required = false;
    }

    // Filter by project ID (through from_page's project association)
    if (filter.project) {
      const projectInclude = [{
        model: db.projects,
        as: 'project',
        required: true,
        where: {
          [Op.or]: [
            { id: { [Op.in]: filter.project.split('|').map(term => Utils.uuid(term)) } },
            {
              name: {
                [Op.or]: filter.project.split('|').map(term => ({ [Op.iLike]: `%${term}%` }))
              }
            },
          ]
        },
      }];
      include[0].include = [...(include[0].include || []), ...projectInclude];
      include[0].required = true;
    }

    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.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 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 = Page_linksDBApi;
