import { Inject, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
import { LmsTableSettingsEntity } from 'src/entities/LmsTableSettings.entity';
import { RECORDS_PER_PAGE } from 'src/constants/urls';
import { IColumnConfig, TableSettings } from 'src/utils/interfaces/config';
import { TableSettingsDto } from '../dto/table-settings-dto';

/**
 * Table Settings Service
 *
 * This service manages user-specific table configurations such as:
 * - Column visibility (show/hide)
 * - Column display order
 * - Pagination preferences
 *
 * Settings are cached in Redis to improve performance and reduce database load.
 * Each user can have different settings for different table types (setting_type).
 *
 * @Injectable - Marks this class as a NestJS injectable service
 */
@Injectable()
export class TableSettingsService {
  constructor(
    @Inject(CACHE_MANAGER) private cacheManager: Cache,
    @InjectRepository(LmsTableSettingsEntity)
    private readonly tableSettingRepo: Repository<LmsTableSettingsEntity>,
  ) {}

  /**
   * Converts a simple column mapping to the full IColumnConfig format.
   *
   * This method is used to create default column configurations when
   * no user-specific settings exist. It converts a simple object format
   * into the standardized column configuration array.
   *
   * @private
   * @param columns - Simple object mapping column keys to titles
   * @returns Array of IColumnConfig objects with all columns enabled by default
   *
   * @example
   * // Input:
   * { name: 'Full Name', email: 'Email Address', status: 'Status' }
   *
   * // Output:
   * [
   *   { key: 'name', title: 'Full Name', is_checked: true },
   *   { key: 'email', title: 'Email Address', is_checked: true },
   *   { key: 'status', title: 'Status', is_checked: true }
   * ]
   */
  private setDefaultColumns(columns: IColumnConfig): IColumnConfig[] {
    // Convert object entries to array of column configs
    return Object.entries(columns).map(([key, title]) => ({
      key,
      title,
      is_checked: true, // All columns visible by default
    }));
  }

  /**
   * Retrieves table settings for a specific user and table type.
   *
   * This method implements a caching strategy:
   * 1. First checks Redis cache
   * 2. If not cached, queries the database
   * 3. Returns cached or fetched data, and caches the result
   *
   * If no user-specific settings exist, it creates default settings
   * based on the provided parameters.
   *
   * @param params - Table settings parameters (user_id, setting_type, columns)
   * @param userId - ID of the authenticated user (should match params.user_id)
   * @returns TableSettings object with columns and pagination configuration
   *
   * @example
   * // Get settings for user 123 on 'user_listing' table
   * const settings = await getTableSettings(
   *   {
   *     user_id: 123,
   *     setting_type: 'user_listing',
   *     columns: { id: 'ID', name: 'Name', email: 'Email' }
   *   },
   *   123
   * );
   *
   * // Returns:
   * {
   *   columns: [
   *     { key: 'id', title: 'ID', is_checked: true },
   *     { key: 'name', title: 'Name', is_checked: true },
   *     { key: 'email', title: 'Email', is_checked: false } // if user hid it
   *   ],
   *   record_per_page: 25
   * }
   */
  async getTableSettings(
    params: TableSettingsDto,
    userId: number,
  ): Promise<TableSettings> {
    // Generate unique cache key based on user and setting type
    const cacheKey = `table_setting:${params.user_id}:${params.setting_type}`;

    // Step 1: Check Redis cache first
    const cachedData = await this.cacheManager.get<TableSettings>(cacheKey);
    if (cachedData) {
      return cachedData; // Return cached data if available
    }

    // Step 2: Query database for user's saved settings
    const userSettings = await this.tableSettingRepo.findOne({
      where: {
        user_id: userId,
        setting_type: params.setting_type,
        // affiliate_user_id: params.affiliate_user_id, // Future: affiliate-specific settings
        // affiliate_user_id: params.is_affiliate ? userId : null, // Alternative approach
      },
    });

    // Step 3: Determine column configuration
    // Use saved settings if available, otherwise create defaults
    const customColumns =
      !userSettings?.setting_pack || userSettings.setting_pack === null
        ? this.setDefaultColumns(params.columns as any) // Create default config
        : userSettings.setting_pack; // Use saved config

    // Step 4: Determine pagination preference
    // Use saved setting if valid, otherwise use application default
    const recordPerPage =
      userSettings?.record_per_page && userSettings.record_per_page > 0
        ? userSettings.record_per_page // User's preferred page size
        : RECORDS_PER_PAGE; // Application default

    // Step 5: Prepare result object
    const result = {
      columns: customColumns,
      record_per_page: recordPerPage,
    };

    // Step 6: Cache the result for future requests
    await this.cacheManager.set(cacheKey, result);

    return result;
  }
}
