import { Injectable, Logger } from '@nestjs/common';
import { ethers } from 'ethers';
import { Web3Service } from './web3.service';
import {
  OnChainApproval,
  ApprovalStatus,
  ApprovalVerification,
} from '../interfaces/on-chain-approval.interface';

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

  private readonly approvalManagerAbi = [
    'function recordApproval(string calldata requestId, address departmentAddress, uint8 status, string calldata remarksHash, string[] calldata documentHashes) public returns (bytes32)',
    'function getRequestApprovals(string calldata requestId) public view returns (tuple(bytes32 id, string requestId, address departmentAddress, uint8 status, uint256 timestamp, string remarksHash, string[] documentHashes)[])',
    'function invalidateApproval(bytes32 approvalId) public',
    'function verifyApproval(bytes32 approvalId, string calldata remarksHash) public view returns (bool)',
    'function getApprovalDetails(bytes32 approvalId) public view returns (tuple(bytes32 id, string requestId, address departmentAddress, uint8 status, uint256 timestamp, string remarksHash, string[] documentHashes))',
  ];

  constructor(private web3Service: Web3Service) {}

  async recordApproval(
    contractAddress: string,
    requestId: string,
    departmentAddress: string,
    approvalStatus: ApprovalStatus,
    remarksHash: string,
    documentHashes: string[],
  ): Promise<string> {
    try {
      this.logger.debug(`Recording approval for request: ${requestId}`);

      const contract = this.web3Service.getContract(
        contractAddress,
        this.approvalManagerAbi,
      ) as any;

      // Map status string to enum number
      const statusMap = {
        [ApprovalStatus.PENDING]: 0,
        [ApprovalStatus.APPROVED]: 1,
        [ApprovalStatus.REJECTED]: 2,
        [ApprovalStatus.CHANGES_REQUESTED]: 3,
        [ApprovalStatus.INVALIDATED]: 4,
      };

      const tx = await contract.recordApproval(
        requestId,
        departmentAddress,
        statusMap[approvalStatus],
        remarksHash,
        documentHashes,
      );

      const receipt = await this.web3Service.sendTransaction(tx);

      this.logger.log(`Approval recorded: ${receipt.hash}`);
      return receipt.hash!;
    } catch (error) {
      this.logger.error(`Failed to record approval for request ${requestId}`, error);
      throw error;
    }
  }

  async getRequestApprovals(
    contractAddress: string,
    requestId: string,
  ): Promise<OnChainApproval[]> {
    try {
      const contract = this.web3Service.getContract(
        contractAddress,
        this.approvalManagerAbi,
      ) as any;

      const approvals = await contract.getRequestApprovals(requestId);

      const statusMap = ['PENDING', 'APPROVED', 'REJECTED', 'CHANGES_REQUESTED', 'INVALIDATED'];

      return approvals.map((approval: any) => ({
        approvalId: approval.id,
        requestId: approval.requestId,
        departmentAddress: approval.departmentAddress,
        status: statusMap[approval.status] as ApprovalStatus,
        timestamp: new Date(Number(approval.timestamp) * 1000),
        remarksHash: approval.remarksHash,
        documentHashes: approval.documentHashes,
        blockNumber: 0, // Would need to get from transaction receipt
        transactionHash: '', // Would need to track separately
      }));
    } catch (error) {
      this.logger.error(`Failed to get approvals for request ${requestId}`, error);
      throw error;
    }
  }

  async invalidateApproval(contractAddress: string, approvalId: string): Promise<string> {
    try {
      this.logger.debug(`Invalidating approval: ${approvalId}`);

      const contract = this.web3Service.getContract(
        contractAddress,
        this.approvalManagerAbi,
      ) as any;

      const tx = await contract.invalidateApproval(approvalId);
      const receipt = await this.web3Service.sendTransaction(tx);

      this.logger.log(`Approval invalidated: ${receipt.hash}`);
      return receipt.hash!;
    } catch (error) {
      this.logger.error(`Failed to invalidate approval ${approvalId}`, error);
      throw error;
    }
  }

  async verifyApproval(
    contractAddress: string,
    approvalId: string,
    remarksHash: string,
  ): Promise<boolean> {
    try {
      const contract = this.web3Service.getContract(
        contractAddress,
        this.approvalManagerAbi,
      ) as any;

      return await contract.verifyApproval(approvalId, remarksHash);
    } catch (error) {
      this.logger.error(`Failed to verify approval ${approvalId}`, error);
      return false;
    }
  }

  async getApprovalDetails(contractAddress: string, approvalId: string): Promise<OnChainApproval> {
    try {
      const contract = this.web3Service.getContract(
        contractAddress,
        this.approvalManagerAbi,
      ) as any;

      const approval = await contract.getApprovalDetails(approvalId);

      const statusMap = ['PENDING', 'APPROVED', 'REJECTED', 'CHANGES_REQUESTED', 'INVALIDATED'];

      return {
        approvalId: approval.id,
        requestId: approval.requestId,
        departmentAddress: approval.departmentAddress,
        status: statusMap[approval.status] as ApprovalStatus,
        timestamp: new Date(Number(approval.timestamp) * 1000),
        remarksHash: approval.remarksHash,
        documentHashes: approval.documentHashes,
        blockNumber: 0,
        transactionHash: '',
      };
    } catch (error) {
      this.logger.error(`Failed to get approval details for ${approvalId}`, error);
      throw error;
    }
  }
}
