import { Inject, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, In, Repository } from 'typeorm';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
import { StudentLoginDto } from './dto/student-login-dto';
import { StudentsEntity } from 'src/entities/Students.entity';
import * as crypto from 'crypto';
import { CustomException } from 'src/utils/response';

/**
 * Constants for business logic
 */
const DIVISION_LIST = [1, 2]; // Example: Valid division IDs
const ADMIN = 'Super Admin'; // Example: Admin role identifier

/**
 * StudentService - Service for student authentication and profile management
 *
 * This service handles student login operations with multiple authentication
 * methods and student profile retrieval. It includes security measures,
 * caching, and business logic validation.
 *
 * ⚠️ SECURITY WARNING: Uses MD5 hashing which is cryptographically broken.
 *                     Should be replaced with bcrypt, Argon2, or PBKDF2.
 *
 * Features:
 * - Multiple login methods (mobile, email, UID)
 * - Password validation and account status checks
 * - Token generation for authenticated sessions
 * - Caching for student profile data
 *
 * @example
 * // Student login
 * const student = await studentService.login(loginDto);
 *
 * @example
 * // Get student profile
 * const profile = await studentService.getStudentFullProfile(12345);
 */
@Injectable()
export class StudentService {
  constructor(
    private datasource: DataSource,
    @Inject(CACHE_MANAGER) private cacheManager: Cache,
    @InjectRepository(StudentsEntity)
    private readonly studentRepository: Repository<StudentsEntity>,
  ) {}

  /**
   * Authenticates a student using one of multiple login methods
   *
   * Validates credentials based on login method, checks account status,
   * generates authentication token, and returns student profile.
   *
   * Security concerns:
   * - Uses MD5 hashing (CRITICAL: Replace with stronger algorithm)
   * - No rate limiting implemented
   * - No password complexity requirements
   *
   * @param params - Login credentials and method selection
   * @returns Student entity with authentication token
   *
   * @throws CustomException - For missing parameters, invalid credentials,
   *                          inactive accounts, unverified accounts, or token failures
   *
   * @example
   * const result = await studentService.login({
   *   login_by: '1',
   *   country_iso3_code: 'IND',
   *   mobile: '9876543210',
   *   password: 'Pass@123'
   * });
   */
  async login(params: StudentLoginDto): Promise<StudentsEntity> {
    const { login_by, country_iso3_code, mobile, email, uid, password } =
      params;

    // Validate login method is provided
    if (!login_by) throw new CustomException('Required Parameter Missing');

    // ⚠️ CRITICAL SECURITY ISSUE: MD5 is cryptographically broken
    // Use bcrypt, Argon2, or PBKDF2 instead
    const passwordHash = crypto
      .createHash('md5')
      .update(password || '')
      .digest('hex');

    let loginCondition: Partial<StudentsEntity> = {};

    // Determine login method and build query condition
    switch (login_by) {
      case '1': // mobile login
        if (country_iso3_code && mobile && password) {
          loginCondition = {
            country_iso3_code: country_iso3_code,
            mobile,
            password: passwordHash, // Compare with MD5 hash
          };
        } else {
          throw new CustomException('Required Parameter Missing');
        }
        break;

      case '2': // email login
        if (email && password) {
          loginCondition = { email, password: passwordHash };
        } else {
          throw new CustomException('Required Parameter Missing');
        }
        break;

      case '3': // UID login
        if (uid && password) {
          loginCondition = { UID: uid, password: passwordHash };
        } else {
          throw new CustomException('Required Parameter Missing');
        }
        break;

      default:
        throw new CustomException('Required Parameter Missing');
    }

    // Find student with credentials
    const studentData = await this.studentRepository.findOne({
      where: loginCondition,
    });

    // Authentication failed
    if (!studentData) {
      throw new CustomException(`Invalid credentials!`);
    }

    // Check account status
    if (!studentData.active) {
      throw new CustomException(`Your account is inactive.`);
    }

    if (!studentData.is_verified) {
      throw new CustomException(
        `Your account is not verified! Please verify your account.`,
      );
    }

    // Generate authentication token
    const token = await this.autoSetLoginToken(studentData.id);
    if (!token) {
      throw new CustomException(`Token Generation Failed.`);
    }

    // Get full profile and add token
    const studentInfo = await this.getStudentFullProfile(studentData.id);
    return { ...studentInfo, token };
  }

  /**
   * Generates authentication token for student
   *
   * Creates a secure random token for authenticated sessions.
   * Should be stored in database or Redis for validation.
   *
   * @param studentId - Student identifier
   * @returns Authentication token string
   *
   * @note Incomplete implementation - token is generated but not stored/validated
   */
  async autoSetLoginToken(studentId: number): Promise<string> {
    // Generate cryptographically secure random token
    const token = crypto.randomBytes(32).toString('hex');

    // TODO: Save token to database or Redis for validation
    // Example: await this.tokenRepository.save({ studentId, token });
    // Example: await this.cacheManager.set(`auth_token:${studentId}`, token);

    return token;
  }

  /**
   * Retrieves complete student profile with caching
   *
   * Fetches student entity from database with caching layer to
   * improve performance for frequently accessed profiles.
   *
   * @param studentId - Student identifier
   * @returns Student entity with all fields
   */
  async getStudentFullProfile(studentId: number): Promise<any> {
    const cacheKey = `student_${studentId}`;

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

    // Fetch from database
    const student = await this.studentRepository.findOne({
      where: { id: studentId },
    });

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

    return student;
  }
}
