import { z } from 'zod';
import { BasePack } from '../base-pack.js';
import type { TwilioClient } from '../../client.js';
import * as renderer from '../../apps/renderer.js';
import { toE164, isValidPhone } from '../../utils/validators.js';

export class IntelligencePack extends BasePack {
  readonly name = 'intelligence';
  readonly description = 'Phone number intelligence, fraud detection, and validation tools';
  readonly keywords = ['lookup', 'validate', 'fraud', 'identity', 'intel', 'verification', 'check'];

  constructor(client: TwilioClient) {
    super(client);
  }

  setup(): void {
    // ─── twilio_deep_scan ───
    this.registerTool({
      name: 'twilio_deep_scan',
      description: 'Comprehensive phone number intelligence report: line type, carrier, caller name, SIM swap risk, fraud indicators, identity match, and more.',
      category: 'intelligence',
      safety: 'green',
      tier: 2,
      isApp: true,
      annotations: { readOnlyHint: true, costHint: true, estimatedCost: '~$0.02-0.05 per scan' },
      inputSchema: z.object({
        phone_number: z.string().describe('Phone number to scan'),
        identity_check: z.object({
          first_name: z.string().optional(),
          last_name: z.string().optional(),
          address_line1: z.string().optional(),
          city: z.string().optional(),
          state: z.string().optional(),
          postal_code: z.string().optional(),
          country_code: z.string().optional(),
        }).optional().describe('Identity information to verify against this number'),
      }),
      _meta: {
        labels: {
          category: "intelligence",
          access: "read",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        const phone = toE164(params.phone_number);
        if (!isValidPhone(phone)) {
          return this.errorResult(`Invalid phone number: ${params.phone_number}`);
        }

        try {
          // Build fields list for Lookup v2
          const fields = [
            'line_type_intelligence',
            'caller_name',
            'sim_swap',
            'line_status',
            'sms_pumping_risk',
            'reassigned_number',
          ];

          // Add identity_match if identity_check provided
          if (params.identity_check) {
            fields.push('identity_match');
          }

          // Execute lookup with all fields
          const lookupParams: any = { fields: fields.join(',') };
          
          // Add identity match parameters if provided
          if (params.identity_check) {
            if (params.identity_check.first_name) lookupParams.firstName = params.identity_check.first_name;
            if (params.identity_check.last_name) lookupParams.lastName = params.identity_check.last_name;
            if (params.identity_check.address_line1) lookupParams.addressLine1 = params.identity_check.address_line1;
            if (params.identity_check.city) lookupParams.city = params.identity_check.city;
            if (params.identity_check.state) lookupParams.state = params.identity_check.state;
            if (params.identity_check.postal_code) lookupParams.postalCode = params.identity_check.postal_code;
            if (params.identity_check.country_code) lookupParams.countryCode = params.identity_check.country_code;
          }

          const result: any = await this.client.execute(c =>
            (c as any).lookups.v2.phoneNumbers(phone).fetch(lookupParams)
          );

          // Parse all the intelligence data
          const lineType = result.lineTypeIntelligence?.type || 'unknown';
          const carrier = result.lineTypeIntelligence?.carrier_name || 'unknown';
          const mobileCountryCode = result.lineTypeIntelligence?.mobile_country_code || 'N/A';
          const mobileNetworkCode = result.lineTypeIntelligence?.mobile_network_code || 'N/A';
          
          const callerName = result.callerName?.caller_name || 'Not available';
          const callerType = result.callerName?.caller_type || 'unknown';
          
          const simSwapError = result.simSwap?.error_code;
          const simSwapLastDate = result.simSwap?.last_swap_date || 'N/A';
          const simSwapStatus = simSwapError ? 'Check unavailable' : (simSwapLastDate === 'N/A' ? 'No recent swaps' : `Last swap: ${simSwapLastDate}`);
          
          const lineStatus = result.lineStatus?.status || 'unknown';
          
          const smsPumpingRisk = result.smsPumpingRisk?.risk_level || 'unknown';
          
          const reassignedNumber = result.reassignedNumber?.is_reassigned || false;
          
          // Identity match scores (if provided)
          let identityMatchHtml = '';
          let identityTextSummary = '';
          if (result.identityMatch) {
            const firstNameScore = result.identityMatch.first_name_match || 0;
            const lastNameScore = result.identityMatch.last_name_match || 0;
            const addressScore = result.identityMatch.address_match || 0;
            const overallScore = Math.round((firstNameScore + lastNameScore + addressScore) / 3);
            
            const scoreColor = (score: number) => score >= 80 ? 'success' : score >= 50 ? 'warning' : 'danger';
            
            identityMatchHtml = `
              <div class="card">
                <div class="card-header">🔐 Identity Match Score</div>
                <div class="card-grid card-grid-4" style="margin-top: 12px;">
                  ${renderer.metricCard(String(overallScore), 'Overall Score')}
                  ${renderer.metricCard(String(firstNameScore), 'First Name')}
                  ${renderer.metricCard(String(lastNameScore), 'Last Name')}
                  ${renderer.metricCard(String(addressScore), 'Address')}
                </div>
                <div style="margin-top: 12px;">
                  ${renderer.badge(overallScore >= 80 ? 'High Confidence Match' : overallScore >= 50 ? 'Partial Match' : 'Low Confidence', scoreColor(overallScore))}
                </div>
              </div>
            `;
            identityTextSummary = `Identity Match: ${overallScore}% overall (First: ${firstNameScore}%, Last: ${lastNameScore}%, Address: ${addressScore}%)`;
          }

          // Calculate overall risk score
          let riskScore = 0;
          const riskFactors: string[] = [];
          
          if (smsPumpingRisk === 'high') { riskScore += 40; riskFactors.push('High SMS pumping risk'); }
          else if (smsPumpingRisk === 'medium') { riskScore += 20; riskFactors.push('Medium SMS pumping risk'); }
          
          if (simSwapLastDate !== 'N/A' && !simSwapError) { riskScore += 30; riskFactors.push('Recent SIM swap detected'); }
          
          if (reassignedNumber) { riskScore += 25; riskFactors.push('Number has been reassigned'); }
          
          if (lineStatus === 'inactive') { riskScore += 15; riskFactors.push('Line is inactive'); }
          
          if (lineType === 'voip') { riskScore += 10; riskFactors.push('VoIP number'); }
          
          const riskLevel = riskScore >= 70 ? 'critical' : riskScore >= 40 ? 'high' : riskScore >= 20 ? 'medium' : 'low';
          const riskColor = riskLevel === 'critical' ? 'danger' : riskLevel === 'high' ? 'warning' : riskLevel === 'medium' ? 'info' : 'success';
          const riskLabel = riskLevel.toUpperCase();

          // Build HTML app
          const html = renderer.appLayout(
            '🔍 Deep Intelligence Scan',
            `Complete analysis of ${phone}`,
            `
            <div class="card-grid card-grid-4">
              ${renderer.metricCard(result.valid ? '✓ Valid' : '✗ Invalid', 'Status')}
              ${renderer.metricCard(lineType.toUpperCase(), 'Line Type')}
              ${renderer.metricCard(carrier, 'Carrier')}
              ${renderer.metricCard(result.countryCode || 'N/A', 'Country')}
            </div>

            <div class="card">
              <div class="card-header">📊 Basic Information</div>
              <div style="padding: 8px 0;">
                ${renderer.table(
                  ['Field', 'Value'],
                  [
                    ['Phone Number', `<strong>${result.phoneNumber}</strong>`],
                    ['National Format', result.nationalFormat || 'N/A'],
                    ['Valid', result.valid ? renderer.badge('Yes', 'success') : renderer.badge('No', 'danger')],
                    ['Country Code', result.countryCode || 'N/A'],
                  ]
                )}
              </div>
            </div>

            <div class="card-grid card-grid-2">
              <div class="card">
                <div class="card-header">📱 Line Type & Carrier</div>
                <div style="padding: 8px 0;">
                  ${renderer.table(
                    ['Field', 'Value'],
                    [
                      ['Line Type', renderer.badge(lineType.toUpperCase(), lineType === 'mobile' ? 'success' : lineType === 'voip' ? 'warning' : 'info')],
                      ['Carrier', carrier],
                      ['MCC', mobileCountryCode],
                      ['MNC', mobileNetworkCode],
                    ]
                  )}
                </div>
              </div>

              <div class="card">
                <div class="card-header">👤 Caller Name</div>
                <div style="padding: 8px 0;">
                  ${renderer.table(
                    ['Field', 'Value'],
                    [
                      ['Name', callerName],
                      ['Type', callerType],
                    ]
                  )}
                </div>
              </div>
            </div>

            <div class="card-grid card-grid-2">
              <div class="card">
                <div class="card-header">🔄 SIM Swap Risk</div>
                <div style="padding: 12px;">
                  <div style="margin-bottom: 8px;">
                    ${simSwapError ? renderer.badge('Check Unavailable', 'muted') : 
                      simSwapLastDate === 'N/A' ? renderer.badge('No Recent Swaps', 'success') : 
                      renderer.badge('Swap Detected', 'danger')}
                  </div>
                  <div class="small muted">
                    ${simSwapStatus}
                  </div>
                </div>
              </div>

              <div class="card">
                <div class="card-header">📡 Line Status</div>
                <div style="padding: 12px;">
                  <div style="margin-bottom: 8px;">
                    ${renderer.badge(lineStatus.toUpperCase(), lineStatus === 'active' ? 'success' : lineStatus === 'inactive' ? 'danger' : 'warning')}
                  </div>
                  <div class="small muted">
                    ${reassignedNumber ? '⚠️ Number has been reassigned' : '✓ Original assignment'}
                  </div>
                </div>
              </div>
            </div>

            ${identityMatchHtml}

            <div class="card">
              <div class="card-header">🚨 Fraud Risk Assessment</div>
              <div style="padding: 12px;">
                <div class="card-grid card-grid-2">
                  <div>
                    <div class="metric-value" style="color: ${riskLevel === 'critical' ? '#ef4444' : riskLevel === 'high' ? '#f59e0b' : riskLevel === 'medium' ? '#3b82f6' : '#22c55e'}">
                      ${riskScore}
                    </div>
                    <div class="metric-label">Risk Score (0-100)</div>
                    <div style="margin-top: 8px;">
                      ${renderer.badge(riskLabel, riskColor)}
                    </div>
                  </div>
                  <div>
                    <div class="small" style="margin-bottom: 6px;"><strong>Risk Factors:</strong></div>
                    ${riskFactors.length > 0 ? riskFactors.map(f => `<div class="small muted">• ${f}</div>`).join('') : '<div class="small muted">No significant risk factors</div>'}
                  </div>
                </div>
                <div style="margin-top: 12px;">
                  <div class="small muted">SMS Pumping Risk: ${renderer.badge(smsPumpingRisk.toUpperCase(), smsPumpingRisk === 'high' ? 'danger' : smsPumpingRisk === 'medium' ? 'warning' : 'success')}</div>
                </div>
              </div>
            </div>
            `
          );

          const textSummary = `Deep Scan Results for ${phone}:\n` +
            `Valid: ${result.valid ? 'Yes' : 'No'}\n` +
            `Line Type: ${lineType} | Carrier: ${carrier}\n` +
            `Caller Name: ${callerName} (${callerType})\n` +
            `SIM Swap: ${simSwapStatus}\n` +
            `Line Status: ${lineStatus}${reassignedNumber ? ' (reassigned)' : ''}\n` +
            `SMS Pumping Risk: ${smsPumpingRisk}\n` +
            `Overall Risk Score: ${riskScore}/100 (${riskLabel})\n` +
            (identityTextSummary ? `${identityTextSummary}\n` : '') +
            (riskFactors.length > 0 ? `Risk Factors: ${riskFactors.join(', ')}` : 'No significant risk factors');

          return this.appResult(html, textSummary);
        } catch (err: any) {
          return this.errorResult(`Deep scan failed: ${err.message}`);
        }
      },
    });

    // ─── twilio_fraud_check ───
    this.registerTool({
      name: 'twilio_fraud_check',
      description: 'Quick fraud risk assessment for a phone number. Checks SMS pumping risk, SIM swap, line status, and reassignment.',
      category: 'intelligence',
      safety: 'green',
      tier: 2,
      annotations: { readOnlyHint: true, costHint: true, estimatedCost: '~$0.01-0.02' },
      inputSchema: z.object({
        phone_number: z.string().describe('Phone number to check'),
        original_date: z.string().optional().describe('ISO date for reassignment check (e.g., 2024-01-15)'),
      }),
      _meta: {
        labels: {
          category: "intelligence",
          access: "read",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        const phone = toE164(params.phone_number);
        if (!isValidPhone(phone)) {
          return this.errorResult(`Invalid phone number: ${params.phone_number}`);
        }

        try {
          const fields = ['sms_pumping_risk', 'sim_swap', 'line_status', 'reassigned_number'];
          
          const lookupParams: any = { fields: fields.join(',') };
          if (params.original_date) {
            lookupParams.lastVerifiedDate = params.original_date;
          }

          const result: any = await this.client.execute(c =>
            (c as any).lookups.v2.phoneNumbers(phone).fetch(lookupParams)
          );

          // Analyze fraud indicators
          const smsPumpingRisk = result.smsPumpingRisk?.risk_level || 'unknown';
          const simSwapError = result.simSwap?.error_code;
          const simSwapLastDate = result.simSwap?.last_swap_date;
          const lineStatus = result.lineStatus?.status || 'unknown';
          const isReassigned = result.reassignedNumber?.is_reassigned || false;

          const reasons: string[] = [];
          let riskLevel: 'low' | 'medium' | 'high' | 'critical' = 'low';
          let recommendation: 'safe' | 'caution' | 'block' = 'safe';

          // Evaluate risk
          if (smsPumpingRisk === 'high') {
            reasons.push('High SMS pumping risk detected');
            riskLevel = 'critical';
            recommendation = 'block';
          } else if (smsPumpingRisk === 'medium') {
            reasons.push('Medium SMS pumping risk detected');
            if (riskLevel === 'low') riskLevel = 'medium';
            if (recommendation === 'safe') recommendation = 'caution';
          }

          if (simSwapLastDate && !simSwapError) {
            const swapDate = new Date(simSwapLastDate);
            const daysSinceSwap = Math.floor((Date.now() - swapDate.getTime()) / (1000 * 60 * 60 * 24));
            if (daysSinceSwap <= 7) {
              reasons.push(`Recent SIM swap detected (${daysSinceSwap} days ago)`);
              if (riskLevel === 'low' || riskLevel === 'medium') riskLevel = 'high';
              recommendation = 'block';
            } else if (daysSinceSwap <= 30) {
              reasons.push(`SIM swap detected ${daysSinceSwap} days ago`);
              if (riskLevel === 'low') riskLevel = 'medium';
              if (recommendation === 'safe') recommendation = 'caution';
            }
          }

          if (lineStatus === 'inactive') {
            reasons.push('Line is inactive');
            if (riskLevel === 'low') riskLevel = 'medium';
            if (recommendation === 'safe') recommendation = 'caution';
          }

          if (isReassigned) {
            reasons.push('Number has been reassigned to a new user');
            if (riskLevel === 'low' || riskLevel === 'medium') riskLevel = 'high';
            if (recommendation === 'safe') recommendation = 'caution';
          }

          if (reasons.length === 0) {
            reasons.push('No significant fraud indicators detected');
          }

          const summary = {
            phone_number: phone,
            risk_level: riskLevel,
            recommendation: recommendation,
            reasons: reasons,
            details: {
              sms_pumping_risk: smsPumpingRisk,
              sim_swap_detected: !!simSwapLastDate && !simSwapError,
              sim_swap_date: simSwapLastDate || null,
              line_status: lineStatus,
              is_reassigned: isReassigned,
            },
          };

          return this.jsonResult(
            summary,
            `Fraud Check: ${phone}\n` +
            `Risk Level: ${riskLevel.toUpperCase()}\n` +
            `Recommendation: ${recommendation.toUpperCase()}\n` +
            `Reasons:\n${reasons.map(r => `  • ${r}`).join('\n')}`
          );
        } catch (err: any) {
          return this.errorResult(`Fraud check failed: ${err.message}`);
        }
      },
    });

    // ─── twilio_number_validate_batch ───
    this.registerTool({
      name: 'twilio_number_validate_batch',
      description: 'Validate multiple phone numbers at once. Returns summary metrics and detailed status for each number.',
      category: 'intelligence',
      safety: 'green',
      tier: 2,
      isApp: true,
      annotations: { readOnlyHint: true, costHint: true, estimatedCost: 'Free (basic) or ~$0.005/number (with line type)' },
      inputSchema: z.object({
        numbers: z.array(z.string()).describe('Array of phone numbers to validate'),
        check_line_type: z.boolean().optional().describe('Include line type check (costs extra)'),
      }),
      _meta: {
        labels: {
          category: "intelligence",
          access: "read",
          complexity: "batch"
        }
      },
      handler: async (params) => {
        if (params.numbers.length === 0) {
          return this.errorResult('No numbers provided');
        }

        if (params.numbers.length > 100) {
          return this.errorResult('Maximum 100 numbers per batch');
        }

        try {
          const results = await Promise.all(
            params.numbers.map(async (num: string) => {
              const e164 = toE164(num);
              try {
                const lookupParams: any = {};
                if (params.check_line_type) {
                  lookupParams.fields = 'line_type_intelligence';
                }

                const lookup: any = await this.client.execute(c =>
                  (c as any).lookups.v2.phoneNumbers(e164).fetch(lookupParams)
                );

                return {
                  input: num,
                  e164: lookup.phoneNumber,
                  valid: lookup.valid,
                  country: lookup.countryCode,
                  line_type: lookup.lineTypeIntelligence?.type || null,
                  carrier: lookup.lineTypeIntelligence?.carrier_name || null,
                  error: null,
                };
              } catch (err: any) {
                return {
                  input: num,
                  e164: e164,
                  valid: false,
                  country: null,
                  line_type: null,
                  carrier: null,
                  error: err.message,
                };
              }
            })
          );

          // Calculate metrics
          const validCount = results.filter(r => r.valid).length;
          const invalidCount = results.filter(r => !r.valid).length;
          const mobileCount = results.filter(r => r.line_type === 'mobile').length;
          const landlineCount = results.filter(r => r.line_type === 'landline').length;
          const voipCount = results.filter(r => r.line_type === 'voip').length;

          // Build HTML app
          const html = renderer.appLayout(
            '✅ Batch Number Validation',
            `Validated ${params.numbers.length} phone numbers`,
            `
            <div class="card-grid card-grid-${params.check_line_type ? '5' : '2'}">
              ${renderer.metricCard(String(validCount), 'Valid')}
              ${renderer.metricCard(String(invalidCount), 'Invalid')}
              ${params.check_line_type ? renderer.metricCard(String(mobileCount), 'Mobile') : ''}
              ${params.check_line_type ? renderer.metricCard(String(landlineCount), 'Landline') : ''}
              ${params.check_line_type ? renderer.metricCard(String(voipCount), 'VoIP') : ''}
            </div>

            <div class="card">
              <div class="card-header">📋 Validation Results</div>
              ${renderer.table(
                ['Input', 'E.164 Format', 'Status', ...(params.check_line_type ? ['Line Type', 'Carrier'] : [])],
                results.map(r => [
                  r.input,
                  r.e164,
                  r.valid ? renderer.badge('Valid', 'success') : renderer.badge(r.error || 'Invalid', 'danger'),
                  ...(params.check_line_type ? [
                    r.line_type ? renderer.badge(r.line_type.toUpperCase(), r.line_type === 'mobile' ? 'success' : r.line_type === 'voip' ? 'warning' : 'info') : '—',
                    r.carrier || '—'
                  ] : [])
                ])
              )}
            </div>
            `
          );

          const textSummary = `Batch Validation Results:\n` +
            `Total: ${params.numbers.length} | Valid: ${validCount} | Invalid: ${invalidCount}\n` +
            (params.check_line_type ? `Mobile: ${mobileCount} | Landline: ${landlineCount} | VoIP: ${voipCount}\n` : '') +
            `\nDetails:\n` +
            results.map(r => 
              `  ${r.input} → ${r.valid ? `✓ ${r.e164}` : `✗ ${r.error || 'Invalid'}`}` +
              (params.check_line_type && r.line_type ? ` [${r.line_type}]` : '')
            ).join('\n');

          return this.appResult(html, textSummary);
        } catch (err: any) {
          return this.errorResult(`Batch validation failed: ${err.message}`);
        }
      },
    });

    // ─── twilio_identity_verify ───
    this.registerTool({
      name: 'twilio_identity_verify',
      description: 'Verify identity information against a phone number. Returns match scores for name and address fields.',
      category: 'intelligence',
      safety: 'green',
      tier: 2,
      annotations: { readOnlyHint: true, costHint: true, estimatedCost: '~$0.01-0.02' },
      inputSchema: z.object({
        phone_number: z.string().describe('Phone number to verify'),
        first_name: z.string().describe('First name to verify'),
        last_name: z.string().describe('Last name to verify'),
        address_line1: z.string().optional().describe('Street address'),
        city: z.string().optional().describe('City'),
        state: z.string().optional().describe('State/province'),
        postal_code: z.string().optional().describe('Postal/ZIP code'),
        country_code: z.string().optional().describe('Country code (e.g., US)'),
        date_of_birth: z.string().optional().describe('Date of birth (YYYY-MM-DD)'),
      }),
      _meta: {
        labels: {
          category: "intelligence",
          access: "read",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        const phone = toE164(params.phone_number);
        if (!isValidPhone(phone)) {
          return this.errorResult(`Invalid phone number: ${params.phone_number}`);
        }

        try {
          const lookupParams: any = {
            fields: 'identity_match',
            firstName: params.first_name,
            lastName: params.last_name,
          };

          if (params.address_line1) lookupParams.addressLine1 = params.address_line1;
          if (params.city) lookupParams.city = params.city;
          if (params.state) lookupParams.state = params.state;
          if (params.postal_code) lookupParams.postalCode = params.postal_code;
          if (params.country_code) lookupParams.countryCode = params.country_code;
          if (params.date_of_birth) lookupParams.dateOfBirth = params.date_of_birth;

          const result: any = await this.client.execute(c =>
            (c as any).lookups.v2.phoneNumbers(phone).fetch(lookupParams)
          );

          if (!result.identityMatch) {
            return this.errorResult('Identity match data not available for this number');
          }

          const firstNameScore = result.identityMatch.first_name_match || 0;
          const lastNameScore = result.identityMatch.last_name_match || 0;
          const addressScore = result.identityMatch.address_match || 0;
          const cityScore = result.identityMatch.city_match || 0;
          const stateScore = result.identityMatch.state_match || 0;
          const postalScore = result.identityMatch.postal_code_match || 0;
          const nationalIdScore = result.identityMatch.national_id_match || 0;

          // Calculate overall confidence
          const scores = [firstNameScore, lastNameScore];
          if (params.address_line1) scores.push(addressScore);
          if (params.city) scores.push(cityScore);
          if (params.state) scores.push(stateScore);
          if (params.postal_code) scores.push(postalScore);
          
          const overallScore = Math.round(scores.reduce((a, b) => a + b, 0) / scores.length);
          
          const confidenceLevel = overallScore >= 80 ? 'high' : overallScore >= 50 ? 'medium' : 'low';
          const recommendation = overallScore >= 80 ? 'verified' : overallScore >= 50 ? 'review' : 'reject';

          const summary = {
            phone_number: phone,
            overall_score: overallScore,
            confidence_level: confidenceLevel,
            recommendation: recommendation,
            field_scores: {
              first_name: firstNameScore,
              last_name: lastNameScore,
              ...(params.address_line1 && { address: addressScore }),
              ...(params.city && { city: cityScore }),
              ...(params.state && { state: stateScore }),
              ...(params.postal_code && { postal_code: postalScore }),
              ...(params.date_of_birth && nationalIdScore > 0 && { national_id: nationalIdScore }),
            },
            input: {
              first_name: params.first_name,
              last_name: params.last_name,
              address_line1: params.address_line1,
              city: params.city,
              state: params.state,
              postal_code: params.postal_code,
            },
          };

          return this.jsonResult(
            summary,
            `Identity Verification for ${phone}:\n` +
            `Overall Score: ${overallScore}/100 (${confidenceLevel} confidence)\n` +
            `Recommendation: ${recommendation.toUpperCase()}\n\n` +
            `Field Scores:\n` +
            `  • First Name: ${firstNameScore}%\n` +
            `  • Last Name: ${lastNameScore}%\n` +
            (params.address_line1 ? `  • Address: ${addressScore}%\n` : '') +
            (params.city ? `  • City: ${cityScore}%\n` : '') +
            (params.state ? `  • State: ${stateScore}%\n` : '') +
            (params.postal_code ? `  • Postal Code: ${postalScore}%\n` : '')
          );
        } catch (err: any) {
          return this.errorResult(`Identity verification failed: ${err.message}`);
        }
      },
    });
  }
}
