// GRU Legal Registration Service
// ISIN, CUSIP, QTID registration

import prisma from '@/shared/database/prisma';
import { v4 as uuidv4 } from 'uuid';
import { logger } from '@/infrastructure/monitoring/logger';


export interface LegalRegistrationResult {
  registrationId: string;
  registrationType: string;
  registrationCode: string;
  status: string;
}

export class GruLegalRegistrationService {
  /**
   * Register ISIN
   * Format: DB + GRU Class Code + Check Digit
   * Example: DBGRUCI1234567
   */
  async registerISIN(
    issuanceId: string,
    issuanceClass: string
  ): Promise<string> {
    logger.info('GRU Legal Registration: Registering ISIN', {
      issuanceId,
      issuanceClass,
    });

    // Extract class code
    const classCode = this.getClassCode(issuanceClass);

    // Generate base code: DB + GRU + Class Code + Sequential Number
    const sequential = await this.getNextSequentialNumber('ISIN');
    const baseCode = `DBGRU${classCode}${sequential.toString().padStart(7, '0')}`;

    // Calculate check digit (ISO 6166 algorithm)
    const checkDigit = this.calculateISINCheckDigit(baseCode);
    const isin = `${baseCode}${checkDigit}`;

    // Create registration
    const registrationId = `GRU-REG-${uuidv4()}`;

    await prisma.gru_legal_registrations.create({
      data: {
        registrationId,
        issuanceId,
        registrationType: 'ISIN',
        registrationCode: isin,
        checkDigit,
        status: 'active',
      },
    });

    logger.info('GRU Legal Registration: ISIN registered', { isin, registrationId });

    return isin;
  }

  /**
   * Register CUSIP
   * Format: DBS + 3-digit instrument suffix
   * Example: DBS123
   */
  async registerCUSIP(
    issuanceId: string,
    issuanceClass: string
  ): Promise<string> {
    logger.info('GRU Legal Registration: Registering CUSIP', {
      issuanceId,
      issuanceClass,
    });

    // Generate 3-digit instrument suffix
    const sequential = await this.getNextSequentialNumber('CUSIP');
    const suffix = sequential.toString().padStart(3, '0');

    // Generate CUSIP: DBS + suffix
    const cusip = `DBS${suffix}`;

    // Calculate check digit
    const checkDigit = this.calculateCUSIPCheckDigit(cusip);
    const fullCusip = `${cusip}${checkDigit}`;

    // Create registration
    const registrationId = `GRU-REG-${uuidv4()}`;

    await prisma.gru_legal_registrations.create({
      data: {
        registrationId,
        issuanceId,
        registrationType: 'CUSIP',
        registrationCode: fullCusip,
        checkDigit,
        status: 'active',
      },
    });

    logger.info('GRU Legal Registration: CUSIP registered', {
      cusip: fullCusip,
      registrationId,
    });

    return fullCusip;
  }

  /**
   * Register QTID (Quantum Token ID)
   * Format: GRU-QNT-XXXX
   */
  async registerQTID(issuanceId: string): Promise<string> {
    logger.info('GRU Legal Registration: Registering QTID', { issuanceId });

    // Generate quantum token identifier
    const sequential = await this.getNextSequentialNumber('QTID');
    const tokenSuffix = sequential.toString(16).toUpperCase().padStart(4, '0');
    const qtid = `GRU-QNT-${tokenSuffix}`;

    // Create registration
    const registrationId = `GRU-REG-${uuidv4()}`;

    await prisma.gru_legal_registrations.create({
      data: {
        registrationId,
        issuanceId,
        registrationType: 'QTID',
        registrationCode: qtid,
        checkDigit: null,
        status: 'active',
      },
    });

    logger.info('GRU Legal Registration: QTID registered', { qtid, registrationId });

    return qtid;
  }

  /**
   * Get class code from issuance class
   */
  private getClassCode(issuanceClass: string): string {
    const codes: Record<string, string> = {
      Class_I: 'CI',
      Class_II: 'CII',
      Class_III: 'CIII',
      Class_IV: 'CIV',
    };

    return codes[issuanceClass] || 'CIV';
  }

  /**
   * Calculate ISIN check digit (ISO 6166)
   */
  private calculateISINCheckDigit(baseCode: string): string {
    // Convert letters to numbers (A=10, B=11, ..., Z=35)
    const digits: number[] = [];
    for (const char of baseCode) {
      if (char >= 'A' && char <= 'Z') {
        digits.push(char.charCodeAt(0) - 55);
      } else if (char >= '0' && char <= '9') {
        digits.push(parseInt(char));
      }
    }

    // Every other digit is doubled
    let sum = 0;
    for (let i = 0; i < digits.length; i++) {
      let value = digits[i];
      if ((digits.length - i) % 2 === 0) {
        value *= 2;
        if (value > 9) {
          value = Math.floor(value / 10) + (value % 10);
        }
      }
      sum += value;
    }

    // Check digit is (10 - (sum % 10)) % 10
    const checkDigit = (10 - (sum % 10)) % 10;
    return checkDigit.toString();
  }

  /**
   * Calculate CUSIP check digit
   */
  private calculateCUSIPCheckDigit(baseCode: string): string {
    // CUSIP check digit calculation
    let sum = 0;
    for (let i = 0; i < baseCode.length; i++) {
      const char = baseCode[i];
      let value: number;

      if (char >= 'A' && char <= 'Z') {
        value = char.charCodeAt(0) - 55;
      } else if (char >= '0' && char <= '9') {
        value = parseInt(char);
      } else {
        continue;
      }

      // Every other character is doubled
      if (i % 2 === 1) {
        value *= 2;
        if (value > 9) {
          value = Math.floor(value / 10) + (value % 10);
        }
      }

      sum += value;
    }

    // Check digit is (10 - (sum % 10)) % 10
    const checkDigit = (10 - (sum % 10)) % 10;
    return checkDigit.toString();
  }

  /**
   * Get next sequential number for registration type
   * In production, this would use a proper sequence or counter
   */
  private async getNextSequentialNumber(registrationType: string): Promise<number> {
    // Get the last registration of this type
    const lastRegistration = await prisma.gru_legal_registrations.findFirst({
      where: { registrationType },
      orderBy: { createdAt: 'desc' },
    });

    if (!lastRegistration) {
      return 1;
    }

    // Extract number from code
    const code = lastRegistration.registrationCode;
    const match = code.match(/\d+/);
    if (match) {
      return parseInt(match[0]) + 1;
    }

    return 1;
  }

  /**
   * Get registration by code
   */
  async getRegistrationByCode(registrationCode: string) {
    return await prisma.gru_legal_registrations.findUnique({
      where: { registrationCode },
      include: {
        issuance: true,
      },
    });
  }

  /**
   * Get registrations for issuance
   */
  async getRegistrationsForIssuance(issuanceId: string) {
    return await prisma.gru_legal_registrations.findMany({
      where: { issuanceId },
      orderBy: { registrationType: 'asc' },
    });
  }

  /**
   * Verify registration code
   */
  async verifyRegistrationCode(
    registrationCode: string,
    registrationType: string
  ): Promise<boolean> {
    const registration = await this.getRegistrationByCode(registrationCode);
    if (!registration) {
      return false;
    }

    if (registration.registrationType !== registrationType) {
      return false;
    }

    if (registration.status !== 'active') {
      return false;
    }

    // Verify check digit if applicable
    if (registration.checkDigit) {
      if (registrationType === 'ISIN') {
        const baseCode = registrationCode.slice(0, -1);
        const calculatedCheckDigit = this.calculateISINCheckDigit(baseCode);
        return calculatedCheckDigit === registration.checkDigit;
      } else if (registrationType === 'CUSIP') {
        const baseCode = registrationCode.slice(0, -1);
        const calculatedCheckDigit = this.calculateCUSIPCheckDigit(baseCode);
        return calculatedCheckDigit === registration.checkDigit;
      }
    }

    return true;
  }
}

export const gruLegalRegistrationService = new GruLegalRegistrationService();

