const GenericDBApi = require('./base.api');
const db = require('../models');
const Utils = require('../utils');
const { 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',
    ];
  }

  static get RANGE_FIELDS() {
    return [];
  }

  static get ENUM_FIELDS() {
    return [];
  }

  static get CSV_FIELDS() {
    return [
      'id',
      'name',
      'slug',
      'description',
      'logo_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,
      logo_url: data.logo_url || null,
      favicon_url: data.favicon_url || null,
      og_image_url: data.og_image_url || 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: '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 runtimeProjectSlug = getRuntimeProjectSlug(options);
    const queryWhere = { ...where };

    // Runtime access: filter by project slug
    // Skip if finding by ID (unambiguous lookup)
    if (runtimeProjectSlug && !where.id) {
      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 });
  }

  /**
   * Create a new project and auto-snapshot global element defaults
   */
  static async create(data, options = {}) {
    const transaction = options.transaction;

    // Create the project using parent's create
    const project = await super.create(data, options);

    // Auto-snapshot global element defaults to the new project
    // Errors propagate to service layer → transaction rollback → proper error to client
    const Project_element_defaultsDBApi = require('./project_element_defaults');
    await Project_element_defaultsDBApi.snapshotGlobalDefaults(project.id, {
      ...options,
      transaction,
    });

    return project;
  }

  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) {
      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 };
      }
    }

    // Runtime access: filter by project slug
    const runtimeProjectSlug = getRuntimeProjectSlug(options);

    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;
