import { Inject, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, Repository, Between, Like } from 'typeorm';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
import { UspTextEntity } from 'src/entities/UspText.entity';
import { UspCountEntity } from 'src/entities/UspCount.entity';
import { UspTextDescriptionEntity } from 'src/entities/UspTextDescription.entity';
import { OffersEntity } from 'src/entities/Offers.entity';
import { WebBannerDetailsEntity } from 'src/entities/WebBannerDetails.entity';
import { CountryEntity } from 'src/entities/Country.entity';
import { StudentTestimonialTextEntity } from 'src/entities/StudentTestimonialText.entity';
import { AccoladesAndAssociationsEntity } from 'src/entities/AccoladesAndAssociations.entity';
import { GetWebMediaDto } from './dto/search-web-media.dto';
import { WebMediaEntity } from 'src/entities/WebMedia.entity';
import { OurProductsEntity } from 'src/entities/OurProducts.entity';
import { TvShowsEntity } from 'src/entities/TvShows.entity';

/**
 * HomeService - Service for managing homepage and public content
 *
 * This service handles all business logic for retrieving and caching
 * public-facing content including USPs, banners, testimonials, media, etc.
 *
 * Features:
 * - Comprehensive caching strategy for performance optimization
 * - Dynamic query building with multiple filter options
 * - Data transformation for frontend consumption
 * - TypeORM repository pattern for database operations
 *
 * Caching Strategy:
 * - Cache keys are constructed from query parameters
 * - Cache TTL should be configured in CacheModule
 * - Manual cache invalidation may be needed for content updates
 *
 * @example
 * // Get USP texts for homepage
 * const uspTexts = await homeService.getAllActiveUspTextFrontend('homepage');
 *
 * @example
 * // Get filtered web media
 * const media = await homeService.getAllWebMedia({
 *   upload_time: 'week',
 *   type: '1'
 * });
 */
@Injectable()
export class HomeService {
  constructor(
    private datasource: DataSource,
    @Inject(CACHE_MANAGER) private cacheManager: Cache,
    @InjectRepository(UspTextEntity)
    private readonly uspTextRepo: Repository<UspTextEntity>,
    @InjectRepository(UspCountEntity)
    private readonly uspCountRepo: Repository<UspCountEntity>,
    @InjectRepository(UspTextDescriptionEntity)
    private readonly uspTextDescriptionRepo: Repository<UspTextDescriptionEntity>,
    @InjectRepository(OffersEntity)
    private readonly offerRepo: Repository<OffersEntity>,
    @InjectRepository(WebBannerDetailsEntity)
    private readonly bannerRepo: Repository<WebBannerDetailsEntity>,
    @InjectRepository(CountryEntity)
    private readonly countryRepo: Repository<CountryEntity>,
    @InjectRepository(StudentTestimonialTextEntity)
    private testimonialRepo: Repository<StudentTestimonialTextEntity>,
    @InjectRepository(AccoladesAndAssociationsEntity)
    private accoladeRepo: Repository<AccoladesAndAssociationsEntity>,
    @InjectRepository(WebMediaEntity)
    private webMediaRepo: Repository<WebMediaEntity>,
    @InjectRepository(OurProductsEntity)
    private ourProductsRepository: Repository<OurProductsEntity>,
    @InjectRepository(TvShowsEntity)
    private readonly tvShowRepository: Repository<TvShowsEntity>,
  ) {}

  /**
   * Get USP text description by page type
   *
   * Retrieves a single USP text description for a specific page type.
   * Uses caching with key based on pageType and optional ID.
   *
   * @param pageType - The page/section type (e.g., 'homepage', 'about')
   * @param id - Optional specific USP description ID
   * @returns Single USP text description entity or null
   */
  async getUspTextDescriptionByPageTypeFrontend(
    pageType: string,
    id?: string,
  ): Promise<UspTextDescriptionEntity> {
    const cacheKey = `active_usp_text_description:${pageType ?? 'all'}:${id ?? 'all'}`;

    // Check cache first
    const cachedData =
      await this.cacheManager.get<UspTextDescriptionEntity>(cacheKey);
    if (cachedData) {
      return cachedData;
    }

    // Build query
    const query = this.uspTextDescriptionRepo
      .createQueryBuilder('usp')
      .where('usp.pagetype = :pageType', { pageType })
      .andWhere('usp.active = :active', { active: '1' });

    // Add ID filter if provided
    const utdId = Number(id);
    if (utdId && utdId !== 0) {
      query.andWhere('usp.utd_id = :id', { id });
    }

    const result = await query.getOne();

    // Cache the result
    await this.cacheManager.set(cacheKey, result);

    return result;
  }

  /**
   * Get all active USP texts
   *
   * Retrieves active Unique Selling Proposition texts with optional
   * filtering by page type and country.
   *
   * @param pageType - Optional page type filter
   * @param visaProductCountryId - Optional country ID filter
   * @returns Array of active USP text entities
   */
  async getAllActiveUspTextFrontend(
    pageType?: string,
    visaProductCountryId?: number,
  ): Promise<UspTextEntity[]> {
    const cacheKey = `active_usp_text:${pageType ?? 'all'}:${visaProductCountryId ?? 'all'}`;

    const cachedData = await this.cacheManager.get<UspTextEntity[]>(cacheKey);
    if (cachedData) {
      return cachedData;
    }

    const query = this.uspTextRepo
      .createQueryBuilder('usp')
      .where('usp.active = :active', { active: true })
      .orderBy('usp.sequence', 'ASC')
      .limit(6);

    if (pageType) {
      query.andWhere('usp.pagetype = :pageType', { pageType });
    }

    const visaId = Number(visaProductCountryId);
    if (visaId && visaId !== 0) {
      query.andWhere('usp.visa_product_country_id = :visaProductCountryId', {
        visaProductCountryId,
      });
    }

    const result = await query.getMany();

    await this.cacheManager.set(cacheKey, result);

    return result;
  }

  /**
   * Get all active USP counts
   *
   * Retrieves statistical counts for Unique Selling Propositions.
   * Similar filtering as USP texts.
   *
   * @param pageType - Optional page type filter
   * @param visaProductCountryId - Optional country ID filter
   * @returns Array of USP count entities
   */
  async getAllActiveUspCountsFrontend(
    pageType?: string,
    visaProductCountryId?: number,
  ): Promise<UspCountEntity[]> {
    const cacheKey = `active_usp_text:${pageType ?? 'all'}:${visaProductCountryId ?? 'all'}`;

    const cachedData = await this.cacheManager.get<UspCountEntity[]>(cacheKey);
    if (cachedData) {
      return cachedData;
    }

    const query = this.uspCountRepo
      .createQueryBuilder('usp')
      .where('usp.active = :active', { active: true })
      .orderBy('usp.id', 'DESC')
      .limit(6);

    if (pageType) {
      query.andWhere('usp.pagetype = :pageType', { pageType });
    }

    const visaId = Number(visaProductCountryId);
    if (visaId && visaId !== 0) {
      query.andWhere('usp.visa_product_country_id = :visaProductCountryId', {
        visaProductCountryId,
      });
    }

    const result = await query.getMany();

    await this.cacheManager.set(cacheKey, result);

    return result;
  }

  /**
   * Get offer marquee content
   *
   * Retrieves current active offers for marquee display.
   * Filters by date range (current offers only).
   *
   * @param limit - Optional limit for number of offers
   * @returns Array of offer data with transformed fields
   */
  async getOfferMarqueeFrontend(limit?: number): Promise<any[]> {
    const cacheKey = `offers_marquee_frontend`;

    const cachedData = await this.cacheManager.get<OffersEntity[]>(cacheKey);
    if (cachedData) {
      return cachedData;
    }

    const currentTimestamp = Math.floor(Date.now() / 1000); // UNIX timestamp

    const query = this.offerRepo
      .createQueryBuilder('offer')
      .select([
        'offer.id AS id',
        'offer.subject AS subject',
        'offer.subject AS title',
        'offer.media_file AS media_file',
        'offer.body AS body',
        'offer.body AS description',
        `DATE_FORMAT(FROM_UNIXTIME(offer.startDateStr), '%d-%b-%Y') AS created`,
        `DATE_FORMAT(FROM_UNIXTIME(offer.endDateStr), '%d-%b-%Y') AS ended`,
      ])
      .where('offer.startDateStr <= :now', { now: currentTimestamp })
      .andWhere('offer.endDateStr >= :now', { now: currentTimestamp })
      .andWhere('offer.active = 1')
      .orderBy('offer.id', 'DESC');

    if (limit) {
      query.limit(limit);
    }

    const result = await query.getRawMany();

    await this.cacheManager.set(cacheKey, result);

    return result;
  }

  /**
   * Get all active web banners
   *
   * Retrieves banner content with contextual filtering based on page type.
   * Supports different banner types: visa products, course pages, branches.
   *
   * @param pageType - Page type for contextual banners
   * @param id - Context-specific ID (country, course, or branch ID)
   * @returns Array of web banner entities
   */
  async getAllActiveWebBannersFrontend(
    pageType?: string,
    id?: number,
  ): Promise<WebBannerDetailsEntity[]> {
    const cacheKey = `active_web_banners:${pageType ?? 'all'}:${id ?? 'all'}`;

    const cachedData =
      await this.cacheManager.get<WebBannerDetailsEntity[]>(cacheKey);
    if (cachedData) {
      return cachedData;
    }

    const query = this.bannerRepo
      .createQueryBuilder('banner')
      .where('banner.active = :active', { active: 1 })
      .orderBy('banner.sequence', 'ASC');

    if (pageType) {
      query.andWhere('banner.pagetype = :pageType', { pageType });

      // Context-specific filtering
      if (pageType === 'visa_products' && id) {
        query.andWhere('banner.visa_product_country_id = :id', { id });
      } else if (pageType === 'course_pages' && id) {
        query.andWhere('banner.test_module_id = :id', { id });
      } else if (pageType === 'branch' && id) {
        query.andWhere('banner.center_id = :id', { id });
      }
    }

    const result = await query.getMany();

    await this.cacheManager.set(cacheKey, result);

    return result;
  }

  /**
   * Get countries list
   *
   * Retrieves all countries with timezone relations.
   * Used for country selection in frontend.
   *
   * @returns Array of country entities with timezone data
   */
  async getCountries() {
    const cacheKey = `countries_list_frontend_header`;

    const cachedData = await this.cacheManager.get<CountryEntity[]>(cacheKey);
    if (cachedData) {
      return cachedData;
    }

    const result = await this.countryRepo.find({
      select: ['country_id', 'name', 'country_code', 'currency_code', 'iso3'],
      relations: ['timezones'],
    });

    await this.cacheManager.set(cacheKey, result);

    return result;
  }

  /**
   * Get textual testimonials
   *
   * Retrieves customer testimonials with optional filtering.
   * Transforms data for frontend consumption.
   *
   * @param pageType - Optional page type filter
   * @param visaProductCountryId - Optional country ID filter
   * @returns Transformed testimonial data
   */
  async getTextualTestimonialShortFrontend(
    pageType?: string,
    visaProductCountryId?: number,
  ) {
    const cacheKey = `home_testimonial_short:${pageType ?? 'all'}:${visaProductCountryId ?? 'all'}`;

    const cachedData =
      await this.cacheManager.get<StudentTestimonialTextEntity[]>(cacheKey);
    if (cachedData) {
      return cachedData;
    }

    const qb = this.testimonialRepo
      .createQueryBuilder('stt')
      .leftJoinAndSelect('stt.user_designation', 'd')
      .leftJoinAndSelect('stt.web_category', 'wmc')
      .where('stt.active = :active', { active: '1' });

    if (pageType) {
      qb.andWhere('stt.for_page = :pageType', { pageType: pageType });
    }

    const visaId = Number(visaProductCountryId);
    if (visaId && visaId !== 0) {
      qb.andWhere('stt.visa_product_country_id = :visaProductCountryId', {
        visaProductCountryId: visaProductCountryId,
      });
    }

    qb.orderBy('stt.id', 'DESC').limit(5);

    const result = await qb.getMany();

    // Transform data for frontend
    const response = result.map((item) => ({
      id: item.id,
      name: item.name,
      testimonial_text: item.testimonial_text,
      image: item.image,
      tsmt_date: item.tsmt_date,
      designation_name: item.user_designation?.designation_name,
      web_cat_id: item.web_category?.id,
      title: item.name,
      description: item.testimonial_text,
    }));

    await this.cacheManager.set(cacheKey, response);

    return response;
  }

  /**
   * Get active accolades
   *
   * Retrieves company awards and achievements.
   *
   * @param pageType - Optional page type filter
   * @param visaProductCountryId - Optional country ID filter
   * @returns Transformed accolade data
   */
  async getActiveAccoladesFrontend(
    pageType?: string,
    visaProductCountryId?: number,
  ) {
    const cacheKey = `home_accolades:${pageType ?? 'all'}:${visaProductCountryId ?? 'all'}`;

    const cachedData =
      await this.cacheManager.get<AccoladesAndAssociationsEntity[]>(cacheKey);
    if (cachedData) {
      return cachedData;
    }

    const qb = this.accoladeRepo
      .createQueryBuilder('acc')
      .where('acc.active = :active', { active: 1 });

    if (pageType) {
      qb.andWhere('acc.pagetype = :pageType', { pageType: pageType });
    }

    const visaId = Number(visaProductCountryId);
    if (visaId && visaId !== 0) {
      qb.andWhere('acc.visa_product_country_id = :visaProductCountryId', {
        visaProductCountryId: visaProductCountryId,
      });
    }

    qb.orderBy('acc.created', 'DESC').limit(20);

    const result = await qb.getMany();

    const response = result.map((item) => ({
      image: item.image,
      title: item.title,
    }));

    await this.cacheManager.set(cacheKey, response);

    return response;
  }

  /**
   * Get filtered web media
   *
   * Retrieves media content with extensive filtering options.
   * Builds complex queries based on multiple filter criteria.
   *
   * @param query - Filtering parameters (type, timeframe, search, etc.)
   * @returns Transformed media data with generated URLs
   */
  async getAllWebMedia(query: GetWebMediaDto) {
    const {
      type,
      pageType,
      upload_time,
      search_text,
      countryId,
      test_module_id,
    } = query;
    const cacheKey = `web_media:${type ?? 'all'}:${pageType ?? 'all'}:${upload_time ?? 'all'}:${search_text ?? 'all'}:${countryId ?? 'all'}:${test_module_id ?? 'all'}`;

    const cachedData = await this.cacheManager.get<WebMediaEntity[]>(cacheKey);
    if (cachedData) {
      return cachedData;
    }

    const qb = this.webMediaRepo
      .createQueryBuilder('wm')
      .leftJoinAndSelect('wm.gallery', 'g')
      .leftJoinAndSelect('wm.web_category', 'wmc')
      .where('wm.active = :active', { active: 1 })
      .groupBy('wm.id')
      .orderBy('wm.id', 'DESC');

    // Search filter
    if (search_text) {
      qb.andWhere('wm.title LIKE :search', { search: `%${search_text}%` });
    }

    // Time-based filtering
    if (upload_time) {
      const now = new Date();
      let firstDay: Date;
      let endDay: Date = now;

      switch (upload_time) {
        case 'week':
          firstDay = new Date();
          firstDay.setDate(firstDay.getDate() - firstDay.getDay());
          break;
        case 'month':
          firstDay = new Date(now.getFullYear(), now.getMonth(), 1);
          endDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
          break;
        case 'six_month':
          firstDay = new Date(now.getFullYear(), now.getMonth() - 5, 1);
          endDay = new Date(now.getFullYear(), now.getMonth() + 1, 0);
          break;
        case 'year':
          firstDay = new Date(now.getFullYear(), 0, 1);
          endDay = new Date(now.getFullYear(), 11, 31);
          break;
        case 'last_year':
          firstDay = new Date(now.getFullYear() - 1, 0, 1);
          endDay = new Date(now.getFullYear() - 1, 11, 31);
          break;
      }

      if (firstDay && endDay) {
        qb.andWhere('DATE(wm.modified) BETWEEN :start AND :end', {
          start: firstDay.toISOString().split('T')[0],
          end: endDay.toISOString().split('T')[0],
        });
      }
    }

    // Additional filters
    if (pageType) qb.andWhere('wm.pagetype = :pageType', { pageType });
    if (type) qb.andWhere('wm.type = :type', { type });
    if (countryId)
      qb.andWhere('wm.visa_product_country_id = :countryId', { countryId });
    if (test_module_id)
      qb.andWhere('wm.test_module_id = :testModuleId', {
        testModuleId: test_module_id,
      });

    const data = await qb.getMany();

    // Transform and generate URLs
    const response = data.map((item) => ({
      id: item.id,
      title: item.title,
      image: item.image,
      video_screenshot: item.video_screenshot,
      gallery_alt_tag: item.gallery?.alt_tag,
      web_image_url: item.gallery?.for_web_image
        ? `${process.env.WOSA_BASE_URL}${process.env.GALLERY_IMAGE_PATH}${item.gallery.for_web_image}`
        : null,
      mobile_image_url: item.gallery?.for_mobile_image
        ? `${process.env.WOSA_BASE_URL}${process.env.GALLERY_IMAGE_PATH}${item.gallery.for_mobile_image}`
        : null,
      video_url: item.gallery?.video_file
        ? `${process.env.WOSA_BASE_URL}${process.env.GALLERY_IMAGE_PATH}${item.gallery.video_file}`
        : null,
      type: item.type,
      description: item.description,
      web_catId: item.web_catId,
      test_module_id: item.test_module_id,
      active: item.active,
      created: item.created,
      webCat: item.web_category?.name,
    }));

    await this.cacheManager.set(cacheKey, response);

    return response;
  }

  /**
   * Get all active our products
   *
   * Retrieves product listings with optional filtering.
   * Returns limited fields for frontend display.
   *
   * @param pageType - Optional page type filter
   * @param visaProductCountryId - Optional country ID filter
   * @returns Array of product entities with selected fields
   */
  async getAllOurProductsShortActiveFrontend(
    pageType?: string,
    visaProductCountryId?: number,
  ): Promise<OurProductsEntity[]> {
    const cacheKey = `our_products:${pageType ?? 'all'}:${visaProductCountryId ?? 'all'}`;

    const cachedData =
      await this.cacheManager.get<OurProductsEntity[]>(cacheKey);
    if (cachedData) {
      return cachedData;
    }

    const query = this.ourProductsRepository
      .createQueryBuilder('our_products')
      .select(['our_products.image', 'our_products.title'])
      .where('our_products.active = :active', { active: '1' });

    if (pageType) {
      query.andWhere('our_products.pagetype = :pageType', { pageType });
    }

    const visaId = Number(visaProductCountryId);
    if (visaId && visaId !== 0) {
      query.andWhere(
        'our_products.visa_product_country_id = :visaProductCountryId',
        {
          visaProductCountryId,
        },
      );
    }

    query.orderBy('our_products.created', 'DESC');

    const result = await query.getMany();

    await this.cacheManager.set(cacheKey, result);

    return result;
  }

  /**
   * Get all TV shows
   *
   * Retrieves TV show/video content with optional page type filtering.
   * Returns limited fields for frontend display.
   *
   * @param pageType - Optional page type filter
   * @returns Array of TV show entities with selected fields
   */
  async getAllTvShowsShortActiveFrontend(
    pageType?: string,
  ): Promise<Partial<TvShowsEntity>[]> {
    const cacheKey = `tv-shows:${pageType ?? 'all'}`;

    const cachedData = await this.cacheManager.get<TvShowsEntity[]>(cacheKey);
    if (cachedData) {
      return cachedData;
    }

    const query = this.tvShowRepository
      .createQueryBuilder('tv_show')
      .select([
        'tv_show.image',
        'tv_show.title',
        'tv_show.day',
        'tv_show.timing',
      ])
      .where('tv_show.active = :active', { active: '1' });

    if (pageType) {
      query.andWhere('tv_show.pagetype = :pageType', { pageType });
    }

    query.orderBy('tv_show.created', 'DESC');

    const result = await query.getMany();

    await this.cacheManager.set(cacheKey, result);

    return result;
  }
}
