import { Inject, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, Repository } from 'typeorm';
import {
  getSkipOffset,
  paginate,
} from 'src/config/pagination/pagination.service';
import { SearchReleaseNotesDto } from './dto/search-dto';
import { Response } from 'src/config/pagination/paginationDto';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
import { ReleaseNotesEntity } from 'src/entities/ReleaseNotes.entity';
import { format } from 'date-fns';
import { CreateReleaseNoteDto } from './dto/create-note-dto';
import { plainToInstance } from 'class-transformer';

@Injectable()
export class ReleaseNotesService {
  constructor(
    // Inject DataSource for database operations and transactions
    private dataSource: DataSource,
    // Inject cache manager for caching (though not used in current methods)
    @Inject(CACHE_MANAGER) private cacheManager: Cache,
    // Inject repository for ReleaseNotesEntity to perform CRUD operations
    @InjectRepository(ReleaseNotesEntity)
    private readonly releaseNoteRepo: Repository<ReleaseNotesEntity>,
  ) {}

  /**
   * Retrieves all release notes with filtering, pagination and sorting
   * @param searchDto - Contains search criteria, pagination and filtering options
   * @returns Paginated response of release notes
   */
  async getAllReleaseNotes(
    searchDto: SearchReleaseNotesDto,
  ): Promise<Response<ReleaseNotesEntity>> {
    // Create query builder for ReleaseNotesEntity with alias 'rn'
    const queryBuilder = this.releaseNoteRepo
      .createQueryBuilder('rn')
      .leftJoin('rn.user', 'user'); // Join with user entity

    // Filter by release version if provided
    if (searchDto.release_version) {
      queryBuilder.where('rn.release_version LIKE :search', {
        search: `%${searchDto.release_version}%`, // Case-sensitive LIKE search
      });
    }

    // Filter by release date if provided
    if (searchDto.release_date) {
      // Format date to yyyy-MM-dd for database comparison
      const formattedDate = format(
        new Date(searchDto.release_date),
        'yyyy-MM-dd',
      );
      // Compare only date part (ignoring time) and filter by active status
      queryBuilder.andWhere('DATE(rn.release_datestr) = :releaseDate', {
        releaseDate: formattedDate.toString(),
      });
      queryBuilder.andWhere('rn.status = :status', { status: 1 }); // Only active records
    }

    // Select specific user fields to avoid exposing sensitive data
    queryBuilder.addSelect(['user.fname', 'user.lname', 'user.employeeCode']);

    // Format created date for display purposes
    queryBuilder.addSelect(
      "DATE_FORMAT(rn.created, '%d-%m-%Y')",
      'createdFormatted',
    );

    // Order by modified date (newest first) and release_id as secondary sort
    queryBuilder.orderBy('rn.modified', 'DESC');
    queryBuilder.addOrderBy('rn.release_id', 'DESC');

    // Calculate skip and limit for pagination
    const [skip, limit] = getSkipOffset(searchDto);

    // Execute query with pagination
    const [data, total] = await queryBuilder
      .skip(skip)
      .take(limit)
      .getManyAndCount();

    // Generate pagination metadata
    const pagination = paginate(total, searchDto.per_page, searchDto.page);

    return { list: data, pagination };
  }

  /**
   * Creates a new release note with auto-generated release version
   * Uses transaction to ensure data consistency
   * @param createDto - Data for creating new release note
   * @returns The created release note entity
   */
  async create(createDto: CreateReleaseNoteDto) {
    // Hardcoded user details - should be replaced with actual authenticated user
    const by_user = 40;
    const user_name = `Ashok Kumar`;
    // Note: status logic commented out - could be enabled if needed
    // const releasedStatus = createDto.status === '1' ? 1 : 0;

    // Create query runner for transaction management
    const queryRunner = this.dataSource.createQueryRunner();

    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      // Convert DTO to entity instance with additional fields
      const releaseNote = plainToInstance(ReleaseNotesEntity, {
        ...createDto,
        release_date: new Date().toLocaleDateString('en-GB'), // Format: DD/MM/YYYY
        // status: releasedStatus, // Could be enabled for status-based logic
        by_user, // User ID who created the record
        user_name, // Name of user who created the record
      });

      // Save the initial release note (generates release_id)
      const result = await queryRunner.manager.save(releaseNote);

      // Update the release_version based on generated release_id
      // Format: ERP + 3-digit padded release_id (e.g., ERP001, ERP045)
      await queryRunner.manager.update(
        ReleaseNotesEntity,
        { release_id: result.release_id },
        { release_version: `ERP${String(result.release_id).padStart(3, '0')}` },
      );

      // Commit transaction if everything succeeds
      await queryRunner.commitTransaction();

      return result;
    } catch (err) {
      // Rollback transaction on any error
      await queryRunner.rollbackTransaction();
      throw err;
    } finally {
      // Always release the query runner
      await queryRunner.release();
    }
  }

  /**
   * Updates an existing release note
   * @param id - The ID of the release note to update
   * @param updateDto - Data to update the release note with
   * @returns Update result
   */
  update(id: number, updateDto: CreateReleaseNoteDto) {
    // Convert DTO to entity instance
    const releaseNoteEntity = plainToInstance(ReleaseNotesEntity, {
      ...updateDto,
    });
    // Perform update operation
    return this.releaseNoteRepo.update(id, releaseNoteEntity);
  }
}
