import { Injectable, NotFoundException, ConflictException, Logger, Inject } from '@nestjs/common';
import { Applicant, LicenseRequest, Document } from '../../database/models';
import { CreateApplicantDto, UpdateApplicantDto } from './dto';
import { ERROR_CODES } from '../../common/constants';
import { paginate, PaginationOptions, PaginatedResult } from '../../common/utils/pagination.util';

export interface ApplicantStats {
  totalRequests: number;
  pendingRequests: number;
  approvedLicenses: number;
  documentsUploaded: number;
  blockchainRecords: number;
}

@Injectable()
export class ApplicantsService {
  private readonly logger = new Logger(ApplicantsService.name);

  async create(dto: CreateApplicantDto): Promise<Applicant> {
    const existing = await Applicant.query().findOne({ digilocker_id: dto.digilockerId });
    if (existing) {
      throw new ConflictException({
        code: ERROR_CODES.VALIDATION_ERROR,
        message: 'Applicant with this DigiLocker ID already exists',
      });
    }

    const applicant = await Applicant.query().insert({
      digilockerId: dto.digilockerId,
      name: dto.name,
      email: dto.email,
      phone: dto.phone,
      walletAddress: dto.walletAddress,
      isActive: true,
    });

    this.logger.log(`Created applicant: ${applicant.id}`);
    return applicant;
  }

  async findAll(options: PaginationOptions): Promise<PaginatedResult<Applicant>> {
    const query = Applicant.query().where('is_active', true).orderBy('created_at', 'desc');

    return await paginate(query, options.page, options.limit);
  }

  async findById(id: string): Promise<Applicant | null> {
    return Applicant.query().findById(id);
  }

  async findByDigilockerId(digilockerId: string): Promise<Applicant | null> {
    return Applicant.query().findOne({ digilocker_id: digilockerId });
  }

  async findByEmail(email: string): Promise<Applicant | null> {
    return Applicant.query().findOne({ email });
  }

  async update(id: string, dto: UpdateApplicantDto): Promise<Applicant> {
    const applicant = await this.findById(id);
    if (!applicant) {
      throw new NotFoundException({
        code: ERROR_CODES.APPLICANT_NOT_FOUND,
        message: 'Applicant not found',
      });
    }

    const updated = await Applicant.query().patchAndFetchById(id, {
      ...(dto.name && { name: dto.name }),
      ...(dto.email && { email: dto.email }),
      ...(dto.phone && { phone: dto.phone }),
      ...(dto.walletAddress && { walletAddress: dto.walletAddress }),
    });

    this.logger.log(`Updated applicant: ${id}`);
    return updated;
  }

  async updateWalletAddress(id: string, walletAddress: string): Promise<Applicant> {
    const applicant = await this.findById(id);
    if (!applicant) {
      throw new NotFoundException({
        code: ERROR_CODES.APPLICANT_NOT_FOUND,
        message: 'Applicant not found',
      });
    }

    return Applicant.query().patchAndFetchById(id, { walletAddress });
  }

  async deactivate(id: string): Promise<void> {
    const applicant = await this.findById(id);
    if (!applicant) {
      throw new NotFoundException({
        code: ERROR_CODES.APPLICANT_NOT_FOUND,
        message: 'Applicant not found',
      });
    }

    await Applicant.query().patchAndFetchById(id, { isActive: false });
    this.logger.log(`Deactivated applicant: ${id}`);
  }

  async getStats(id: string): Promise<ApplicantStats> {
    const applicant = await this.findById(id);
    if (!applicant) {
      throw new NotFoundException({
        code: ERROR_CODES.APPLICANT_NOT_FOUND,
        message: 'Applicant not found',
      });
    }

    // Get all requests for this applicant
    const requests = await LicenseRequest.query().where('applicant_id', id);

    // Count pending requests (SUBMITTED, IN_REVIEW)
    const pendingRequests = requests.filter(r =>
      ['SUBMITTED', 'IN_REVIEW'].includes(r.status),
    ).length;

    // Count approved licenses
    const approvedLicenses = requests.filter(r =>
      ['APPROVED', 'COMPLETED'].includes(r.status),
    ).length;

    // Count blockchain records (requests with transaction hash)
    const blockchainRecords = requests.filter(r => r.blockchainTxHash).length;

    // Count documents for all requests
    const requestIds = requests.map(r => r.id);
    let documentsUploaded = 0;
    if (requestIds.length > 0) {
      const documents = await Document.query()
        .whereIn('request_id', requestIds)
        .where('is_active', true);
      documentsUploaded = documents.length;
    }

    return {
      totalRequests: requests.length,
      pendingRequests,
      approvedLicenses,
      documentsUploaded,
      blockchainRecords,
    };
  }
}
