import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { ApprovalRepository } from '../../infrastructure/repositories/approval.repository';
import { TaskRepository } from '../../infrastructure/repositories/task.repository';
import { ApprovalWorkflowService } from '../../domain/services/approval-workflow.service';
import { ApprovalRequest } from '../../domain/entities/approval-request.entity';
import { RedisEventBus } from '@it0/events';
import { EventPatterns } from '@it0/common';

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

  constructor(
    private readonly approvalRepository: ApprovalRepository,
    private readonly taskRepository: TaskRepository,
    private readonly approvalWorkflowService: ApprovalWorkflowService,
    private readonly eventBus: RedisEventBus,
  ) {}

  async approve(approvalId: string, approvedBy: string): Promise<void> {
    const approval = await this.approvalRepository.findById(approvalId);
    if (!approval) {
      throw new NotFoundException(`Approval request ${approvalId} not found`);
    }

    if (approval.status !== 'pending') {
      throw new Error(`Approval request ${approvalId} is already ${approval.status}`);
    }

    const resolved = this.approvalWorkflowService.resolveApproval(
      approvalId,
      true,
      approvedBy,
    );

    if (!resolved) {
      approval.status = 'approved';
      approval.decidedBy = approvedBy;
      approval.decidedAt = new Date();
      await this.approvalRepository.save(approval);
    }

    this.logger.log(`Approval ${approvalId} approved by ${approvedBy}`);

    await this.eventBus.publish(EventPatterns.APPROVAL_RESOLVED, {
      id: crypto.randomUUID(),
      tenantId: approval.tenantId,
      timestamp: new Date(),
      source: 'ops-service',
      type: 'approval.approved',
      payload: {
        approvalId,
        taskId: approval.taskId,
        approved: true,
        decidedBy: approvedBy,
      },
    });
  }

  async reject(approvalId: string, rejectedBy: string, reason?: string): Promise<void> {
    const approval = await this.approvalRepository.findById(approvalId);
    if (!approval) {
      throw new NotFoundException(`Approval request ${approvalId} not found`);
    }

    if (approval.status !== 'pending') {
      throw new Error(`Approval request ${approvalId} is already ${approval.status}`);
    }

    const resolved = this.approvalWorkflowService.resolveApproval(
      approvalId,
      false,
      rejectedBy,
      reason,
    );

    if (!resolved) {
      approval.status = 'rejected';
      approval.decidedBy = rejectedBy;
      approval.decidedAt = new Date();
      approval.reason = reason;
      await this.approvalRepository.save(approval);
    }

    // Cancel the associated task
    const task = await this.taskRepository.findById(approval.taskId);
    if (task && task.status === 'pending') {
      task.status = 'cancelled';
      await this.taskRepository.save(task);
    }

    this.logger.log(`Approval ${approvalId} rejected by ${rejectedBy}: ${reason ?? 'no reason'}`);

    await this.eventBus.publish(EventPatterns.APPROVAL_RESOLVED, {
      id: crypto.randomUUID(),
      tenantId: approval.tenantId,
      timestamp: new Date(),
      source: 'ops-service',
      type: 'approval.rejected',
      payload: {
        approvalId,
        taskId: approval.taskId,
        approved: false,
        decidedBy: rejectedBy,
        reason,
      },
    });
  }

  async getApprovalRequests(status?: string): Promise<ApprovalRequest[]> {
    if (status === 'pending') {
      return this.approvalRepository.findPending();
    }
    return this.approvalRepository.findAll();
  }
}
