import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, IsNull, FindOptionsWhere } from 'typeorm';
import { ClsService } from 'nestjs-cls';
import { TenantAwareRepository } from '../../../common/repositories/tenant-aware.repository';
import { ActivityPricingEntity } from '../../../entities/activity-pricing.entity';

@Injectable()
export class ActivityPricingRepository extends TenantAwareRepository<ActivityPricingEntity> {
  constructor(
    @InjectRepository(ActivityPricingEntity) repo: Repository<ActivityPricingEntity>,
    cls: ClsService,
  ) {
    super(repo, cls);
  }

  async findByDestinationAndCurrency(
    destinationId?: string,
    currencyId?: string,
  ): Promise<ActivityPricingEntity[]> {
    const tenantId = this.getTenantId();
    const where: FindOptionsWhere<ActivityPricingEntity> = {
      tenant_id: tenantId,
      is_deleted: false,
    };
    if (destinationId) where.destination_id = destinationId;
    if (currencyId) where.currency_id = currencyId;

    return this.repo.find({
      where,
      relations: {
        activity: {
          destination: true,
        },

        ticket: true,
        currency: true,
      },
      order: { age_group: 'ASC' },
    });
  }

  async findPaginatedByActivities(query: {
    destinationId?: string;
    currencyId?: string;
    search?: string;
    page?: number;
    limit?: number;
  }) {
    const tenantId = this.getTenantId();
    const { destinationId, currencyId, search, page = 1, limit = 10 } = query;

    // Step 1: Build base query for distinct activity_ids
    const baseQb = this.repo
      .createQueryBuilder('ap')
      .where('ap.tenant_id = :tenantId AND ap.is_deleted = false', { tenantId });

    if (destinationId) baseQb.andWhere('ap.destination_id = :destinationId', { destinationId });
    if (currencyId) baseQb.andWhere('ap.currency_id = :currencyId', { currencyId });
    if (search) {
      baseQb
        .leftJoin('ap.activity', 'act')
        .andWhere('act.name ILIKE :search', { search: `%${search}%` });
    }

    // Count distinct activities
    const countQb = baseQb.clone().select('COUNT(DISTINCT ap.activity_id)', 'cnt');
    const countResult = await countQb.getRawOne();
    const totalActivities = parseInt(countResult?.cnt || '0', 10);

    // Get paginated distinct activity_ids
    const activityRows = await baseQb
      .select('ap.activity_id', 'activity_id')
      .groupBy('ap.activity_id')
      .addSelect('MAX(ap.created_at)', 'latest')
      .orderBy('latest', 'DESC')
      .offset((page - 1) * limit)
      .limit(limit)
      .getRawMany();

    const activityIds = activityRows.map((r: { activity_id: string }) => r.activity_id);
    if (activityIds.length === 0) {
      return {
        items: [],
        meta: {
          total: totalActivities,
          page,
          limit,
          totalPages: Math.ceil(totalActivities / limit),
        },
      };
    }

    // Step 2: Fetch all pricing rows for those activities
    const items = await this.repo.find({
      where: activityIds.map((id: string) => ({
        tenant_id: tenantId,
        activity_id: id,
        is_deleted: false,
        ...(currencyId ? { currency_id: currencyId } : {}),
      })),
      relations: {
        activity: {
          destination: true,
        },

        ticket: true,
        currency: true,
      },
      order: { age_group: 'ASC' },
    });

    return {
      items,
      meta: { total: totalActivities, page, limit, totalPages: Math.ceil(totalActivities / limit) },
    };
  }

  async findAllForExport(destinationId?: string): Promise<ActivityPricingEntity[]> {
    const tenantId = this.getTenantId();
    // Narrow to a single destination when the caller supplies one so the
    // Download Sample button honours the workspace destination filter
    // — UI would otherwise download every destination's rows regardless
    // of what's selected in the top header.
    //
    // Sample order matches the on-screen ActivityPricingPage grouping:
    //   activity name ASC → ticket name ASC (General first) → age group
    //   in the UX order (child, adult, senior — see AGE_GROUPS on FE).
    // Previously the sample was `age_group ASC` which sorted
    // alphabetically (adult before child), giving finance the
    // "random"-looking file they flagged in UAT 2026-06-30.
    const qb = this.repo
      .createQueryBuilder('ap')
      .leftJoinAndSelect('ap.activity', 'activity')
      .leftJoinAndSelect('activity.destination', 'destination')
      .leftJoinAndSelect('ap.ticket', 'ticket')
      .leftJoinAndSelect('ap.currency', 'currency')
      .where('ap.tenant_id = :tenantId', { tenantId })
      .andWhere('ap.is_deleted = false');
    if (destinationId) {
      qb.andWhere('ap.destination_id = :destinationId', { destinationId });
    }
    qb.orderBy('activity.name', 'ASC')
      .addOrderBy('ticket.name', 'ASC', 'NULLS FIRST')
      .addOrderBy(
        "CASE ap.age_group WHEN 'child' THEN 1 WHEN 'adult' THEN 2 WHEN 'senior' THEN 3 ELSE 4 END",
        'ASC',
      );
    return qb.getMany();
  }

  async findByActivity(activityId: string): Promise<ActivityPricingEntity[]> {
    const tenantId = this.getTenantId();
    return this.repo.find({
      where: { activity_id: activityId, tenant_id: tenantId, is_deleted: false },
    });
  }

  async findExisting(
    activityId: string,
    ticketId: string | null,
    ageGroup: string,
    currencyId: string,
  ): Promise<ActivityPricingEntity | null> {
    const tenantId = this.getTenantId();
    const where: FindOptionsWhere<ActivityPricingEntity> = {
      activity_id: activityId,
      age_group: ageGroup as ActivityPricingEntity['age_group'],
      currency_id: currencyId,
      tenant_id: tenantId,
      is_deleted: false,
    };
    where.ticket_id = ticketId ? ticketId : IsNull();
    return this.repo.findOne({ where });
  }

  async findSoftDeleted(
    activityId: string,
    ticketId: string | null,
    ageGroup: string,
    currencyId: string,
  ): Promise<ActivityPricingEntity | null> {
    const tenantId = this.getTenantId();
    const where: FindOptionsWhere<ActivityPricingEntity> = {
      activity_id: activityId,
      age_group: ageGroup as ActivityPricingEntity['age_group'],
      currency_id: currencyId,
      tenant_id: tenantId,
      is_deleted: true,
    };
    where.ticket_id = ticketId ? ticketId : IsNull();
    return this.repo.findOne({ where });
  }

  async reactivate(id: string, data: Partial<ActivityPricingEntity>): Promise<void> {
    const userId = this.getUserId();
    await this.repo.update(id, { ...data, is_deleted: false, updated_by: userId });
  }
}
