/**
 * twilio-client.ts — Wrapper around Twilio SDK
 * Handles authentication and provides typed methods for TrustHub + A2P Messaging API
 */

import Twilio from 'twilio';
import type { Twilio as TwilioClient } from 'twilio';
import type {
  BusinessInfo,
  AuthorizedRep,
  BusinessAddress,
  CampaignInfo,
} from '../types';
import pino from 'pino';

const logger = pino({ name: 'twilio-client' });

// ============================================================
// CONFIGURATION
// ============================================================

export interface TwilioConfig {
  accountSid: string;
  authToken: string;
  primaryCustomerProfileSid?: string; // Primary CustomerProfile for all brands
  a2pPolicySid?: string;             // Policy SID for A2P messaging (RN...)
}

// ============================================================
// TWILIO CLIENT WRAPPER
// ============================================================

export class TwilioApiClient {
  private client: TwilioClient;
  private config: TwilioConfig;

  constructor(config: TwilioConfig) {
    this.config = config;
    this.client = Twilio(config.accountSid, config.authToken);
    logger.info({ accountSid: config.accountSid }, 'Twilio client initialized');
  }

  // ============================================================
  // STEP 1: CREATE SECONDARY CUSTOMER PROFILE
  // ============================================================

  /**
   * Creates a Secondary CustomerProfile (Business Bundle)
   * This represents the business entity being registered.
   */
  async createSecondaryCustomerProfile(
    businessName: string,
    email: string
  ): Promise<{ sid: string; status: string }> {
    try {
      logger.info({ businessName, email }, 'Creating Secondary CustomerProfile');

      const profile = await this.client.trusthub.v1.customerProfiles.create({
        friendlyName: businessName,
        email: email,
        policySid: 'RNdfbf3fae0e1107f8aded0e7cead80bf5', // Secondary CustomerProfile policy
      });

      logger.info({ sid: profile.sid, status: profile.status }, 'Secondary CustomerProfile created');
      return { sid: profile.sid, status: profile.status };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to create Secondary CustomerProfile');
      throw new Error(`Failed to create Secondary CustomerProfile: ${error.message}`);
    }
  }

  // ============================================================
  // STEP 2: CREATE BUSINESS END USER
  // ============================================================

  /**
   * Creates an EndUser with business information
   * Type: customer_profile_business_information
   */
  async createBusinessEndUser(
    business: BusinessInfo
  ): Promise<{ sid: string }> {
    try {
      logger.info({ businessName: business.businessName }, 'Creating Business EndUser');

      const attributes: Record<string, string> = {
        business_name: business.businessName,
        business_type: business.businessType,
        business_registration_identifier: business.registrationIdentifier,
        business_registration_number: business.registrationNumber,
        business_identity: business.businessIdentity,
        business_industry: business.businessIndustry,
        website_url: business.websiteUrl,
        business_regions_of_operation: business.regionsOfOperation.join(','),
      };

      // Add social media URLs if provided
      if (business.socialMediaUrls && business.socialMediaUrls.length > 0) {
        business.socialMediaUrls.forEach((url, idx) => {
          attributes[`social_media_profile_urls[${idx}]`] = url;
        });
      }

      const endUser = await this.client.trusthub.v1.endUsers.create({
        friendlyName: `${business.businessName} - Business Info`,
        type: 'customer_profile_business_information',
        attributes: attributes,
      });

      logger.info({ sid: endUser.sid }, 'Business EndUser created');
      return { sid: endUser.sid };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to create Business EndUser');
      throw new Error(`Failed to create Business EndUser: ${error.message}`);
    }
  }

  /**
   * Assigns an EndUser to a CustomerProfile
   */
  async assignEndUserToProfile(
    customerProfileSid: string,
    endUserSid: string
  ): Promise<void> {
    try {
      logger.info({ customerProfileSid, endUserSid }, 'Assigning EndUser to CustomerProfile');

      await this.client.trusthub.v1
        .customerProfiles(customerProfileSid)
        .customerProfilesEntityAssignments.create({
          objectSid: endUserSid,
        });

      logger.info('EndUser assigned successfully');
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to assign EndUser');
      throw new Error(`Failed to assign EndUser: ${error.message}`);
    }
  }

  // ============================================================
  // STEP 3: CREATE AUTHORIZED REP END USER
  // ============================================================

  /**
   * Creates an EndUser with authorized representative information
   * Type: authorized_representative_1
   */
  async createAuthorizedRepEndUser(
    rep: AuthorizedRep
  ): Promise<{ sid: string }> {
    try {
      logger.info({ name: `${rep.firstName} ${rep.lastName}` }, 'Creating Authorized Rep EndUser');

      const endUser = await this.client.trusthub.v1.endUsers.create({
        friendlyName: `${rep.firstName} ${rep.lastName} - Authorized Rep`,
        type: 'authorized_representative_1',
        attributes: {
          first_name: rep.firstName,
          last_name: rep.lastName,
          business_title: rep.businessTitle,
          job_position: rep.jobPosition,
          phone_number: rep.phoneNumber,
          email: rep.email,
        },
      });

      logger.info({ sid: endUser.sid }, 'Authorized Rep EndUser created');
      return { sid: endUser.sid };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to create Authorized Rep EndUser');
      throw new Error(`Failed to create Authorized Rep EndUser: ${error.message}`);
    }
  }

  // ============================================================
  // STEP 4: CREATE ADDRESS + SUPPORTING DOCUMENT
  // ============================================================

  /**
   * Creates an Address resource
   */
  async createAddress(
    address: BusinessAddress
  ): Promise<{ sid: string }> {
    try {
      logger.info({ city: address.city, region: address.region }, 'Creating Address');

      const addr = await this.client.addresses.create({
        customerName: address.customerName,
        street: address.street,
        streetSecondary: address.streetSecondary,
        city: address.city,
        region: address.region,
        postalCode: address.postalCode,
        isoCountry: address.isoCountry,
      });

      logger.info({ sid: addr.sid }, 'Address created');
      return { sid: addr.sid };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to create Address');
      throw new Error(`Failed to create Address: ${error.message}`);
    }
  }

  /**
   * Creates a SupportingDocument from an Address
   * Type: customer_profile_address
   */
  async createAddressSupportingDocument(
    addressSid: string,
    businessName: string
  ): Promise<{ sid: string }> {
    try {
      logger.info({ addressSid }, 'Creating Address SupportingDocument');

      const doc = await this.client.trusthub.v1.supportingDocuments.create({
        friendlyName: `${businessName} - Address`,
        type: 'customer_profile_address',
        attributes: {
          address_sid: addressSid,
        },
      });

      logger.info({ sid: doc.sid }, 'Address SupportingDocument created');
      return { sid: doc.sid };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to create Address SupportingDocument');
      throw new Error(`Failed to create Address SupportingDocument: ${error.message}`);
    }
  }

  /**
   * Assigns a SupportingDocument to a CustomerProfile
   */
  async assignSupportingDocToProfile(
    customerProfileSid: string,
    supportingDocSid: string
  ): Promise<void> {
    try {
      logger.info({ customerProfileSid, supportingDocSid }, 'Assigning SupportingDocument to CustomerProfile');

      await this.client.trusthub.v1
        .customerProfiles(customerProfileSid)
        .customerProfilesEntityAssignments.create({
          objectSid: supportingDocSid,
        });

      logger.info('SupportingDocument assigned successfully');
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to assign SupportingDocument');
      throw new Error(`Failed to assign SupportingDocument: ${error.message}`);
    }
  }

  // ============================================================
  // STEP 5: ASSIGN SECONDARY TO PRIMARY
  // ============================================================

  /**
   * Assigns the Secondary CustomerProfile to the Primary CustomerProfile
   */
  async assignSecondaryToPrimary(
    secondaryCustomerProfileSid: string
  ): Promise<void> {
    if (!this.config.primaryCustomerProfileSid) {
      throw new Error('Primary CustomerProfile SID not configured');
    }

    try {
      logger.info(
        { 
          primary: this.config.primaryCustomerProfileSid,
          secondary: secondaryCustomerProfileSid 
        },
        'Assigning Secondary to Primary CustomerProfile'
      );

      await this.client.trusthub.v1
        .customerProfiles(this.config.primaryCustomerProfileSid)
        .customerProfilesEntityAssignments.create({
          objectSid: secondaryCustomerProfileSid,
        });

      logger.info('Secondary CustomerProfile assigned to Primary');
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to assign Secondary to Primary');
      throw new Error(`Failed to assign Secondary to Primary: ${error.message}`);
    }
  }

  // ============================================================
  // STEP 6: EVALUATE CUSTOMER PROFILE
  // ============================================================

  /**
   * Evaluates a CustomerProfile for compliance
   * Returns the evaluation results
   */
  async evaluateCustomerProfile(
    customerProfileSid: string
  ): Promise<{ status: string; results: any[] }> {
    try {
      logger.info({ customerProfileSid }, 'Evaluating CustomerProfile');

      const evaluations = await this.client.trusthub.v1
        .customerProfiles(customerProfileSid)
        .customerProfilesEvaluations.create();

      logger.info(
        { status: evaluations.status, results: evaluations.results },
        'CustomerProfile evaluation complete'
      );

      return {
        status: evaluations.status,
        results: evaluations.results,
      };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to evaluate CustomerProfile');
      throw new Error(`Failed to evaluate CustomerProfile: ${error.message}`);
    }
  }

  // ============================================================
  // STEP 7: SUBMIT CUSTOMER PROFILE
  // ============================================================

  /**
   * Submits a CustomerProfile for Twilio review
   */
  async submitCustomerProfile(
    customerProfileSid: string
  ): Promise<{ status: string }> {
    try {
      logger.info({ customerProfileSid }, 'Submitting CustomerProfile');

      const profile = await this.client.trusthub.v1
        .customerProfiles(customerProfileSid)
        .update({ status: 'pending-review' });

      logger.info({ status: profile.status }, 'CustomerProfile submitted');
      return { status: profile.status };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to submit CustomerProfile');
      throw new Error(`Failed to submit CustomerProfile: ${error.message}`);
    }
  }

  // ============================================================
  // STEP 8: CREATE TRUST PRODUCT
  // ============================================================

  /**
   * Creates a TrustProduct for A2P messaging
   */
  async createTrustProduct(
    businessName: string,
    email: string
  ): Promise<{ sid: string; status: string }> {
    if (!this.config.a2pPolicySid) {
      throw new Error('A2P Policy SID not configured');
    }

    try {
      logger.info({ businessName }, 'Creating TrustProduct');

      const trustProduct = await this.client.trusthub.v1.trustProducts.create({
        friendlyName: `${businessName} - A2P Brand`,
        email: email,
        policySid: this.config.a2pPolicySid,
      });

      logger.info({ sid: trustProduct.sid, status: trustProduct.status }, 'TrustProduct created');
      return { sid: trustProduct.sid, status: trustProduct.status };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to create TrustProduct');
      throw new Error(`Failed to create TrustProduct: ${error.message}`);
    }
  }

  /**
   * Creates an A2P Profile EndUser
   * Type: us_a2p_messaging_profile_information
   */
  async createA2PProfileEndUser(
    business: BusinessInfo
  ): Promise<{ sid: string }> {
    try {
      logger.info({ businessName: business.businessName }, 'Creating A2P Profile EndUser');

      const attributes: Record<string, string> = {
        business_name: business.businessName,
        business_type: business.businessType,
        business_registration_identifier: business.registrationIdentifier,
        business_registration_number: business.registrationNumber,
        business_identity: business.businessIdentity,
        business_industry: business.businessIndustry,
        website_url: business.websiteUrl,
        business_regions_of_operation: business.regionsOfOperation.join(','),
        company_type: business.companyType,
      };

      // Add optional fields for public companies
      if (business.companyType === 'public') {
        if (business.stockExchange) attributes.stock_exchange = business.stockExchange;
        if (business.stockTicker) attributes.stock_symbol = business.stockTicker;
        if (business.brandContactEmail) attributes.brand_contact_email = business.brandContactEmail;
      }

      // Add skip auto sec vet flag if provided
      if (business.skipAutoSecVet !== undefined) {
        attributes.skip_automatic_sec_vet = business.skipAutoSecVet.toString();
      }

      const endUser = await this.client.trusthub.v1.endUsers.create({
        friendlyName: `${business.businessName} - A2P Profile`,
        type: 'us_a2p_messaging_profile_information',
        attributes: attributes,
      });

      logger.info({ sid: endUser.sid }, 'A2P Profile EndUser created');
      return { sid: endUser.sid };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to create A2P Profile EndUser');
      throw new Error(`Failed to create A2P Profile EndUser: ${error.message}`);
    }
  }

  /**
   * Assigns an EndUser to a TrustProduct
   */
  async assignEndUserToTrustProduct(
    trustProductSid: string,
    endUserSid: string
  ): Promise<void> {
    try {
      logger.info({ trustProductSid, endUserSid }, 'Assigning EndUser to TrustProduct');

      await this.client.trusthub.v1
        .trustProducts(trustProductSid)
        .trustProductsEntityAssignments.create({
          objectSid: endUserSid,
        });

      logger.info('EndUser assigned to TrustProduct');
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to assign EndUser to TrustProduct');
      throw new Error(`Failed to assign EndUser to TrustProduct: ${error.message}`);
    }
  }

  /**
   * Submits a TrustProduct for review
   */
  async submitTrustProduct(
    trustProductSid: string
  ): Promise<{ status: string }> {
    try {
      logger.info({ trustProductSid }, 'Submitting TrustProduct');

      const trustProduct = await this.client.trusthub.v1
        .trustProducts(trustProductSid)
        .update({ status: 'pending-review' });

      logger.info({ status: trustProduct.status }, 'TrustProduct submitted');
      return { status: trustProduct.status };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to submit TrustProduct');
      throw new Error(`Failed to submit TrustProduct: ${error.message}`);
    }
  }

  // ============================================================
  // STEP 9: CREATE BRAND REGISTRATION
  // ============================================================

  /**
   * Creates an A2P Brand Registration
   * Uses the Messaging API endpoint
   */
  async createBrandRegistration(
    trustProductSid: string,
    skipAutoSecVet?: boolean
  ): Promise<{ sid: string; identityStatus: string }> {
    try {
      logger.info({ trustProductSid }, 'Creating Brand Registration');

      // Call Twilio Messaging API directly for brand registration
      const brand = await this.client.messaging.v1.a2p.brandRegistrations.create({
        customerProfileBundleSid: trustProductSid,
        skipAutomaticSecVet: skipAutoSecVet,
      });

      logger.info(
        { sid: brand.sid, identityStatus: brand.identityStatus },
        'Brand Registration created'
      );

      return {
        sid: brand.sid,
        identityStatus: brand.identityStatus,
      };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to create Brand Registration');
      throw new Error(`Failed to create Brand Registration: ${error.message}`);
    }
  }

  /**
   * Gets the status of a Brand Registration
   */
  async getBrandRegistrationStatus(
    brandSid: string
  ): Promise<{ identityStatus: string; trustScore?: number }> {
    try {
      const brand = await this.client.messaging.v1.a2p.brandRegistrations(brandSid).fetch();

      return {
        identityStatus: brand.identityStatus,
        trustScore: brand.trustScore,
      };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to fetch Brand Registration status');
      throw new Error(`Failed to fetch Brand Registration status: ${error.message}`);
    }
  }

  // ============================================================
  // STEP 10: CREATE MESSAGING SERVICE
  // ============================================================

  /**
   * Creates a Messaging Service
   */
  async createMessagingService(
    friendlyName: string
  ): Promise<{ sid: string }> {
    try {
      logger.info({ friendlyName }, 'Creating Messaging Service');

      const service = await this.client.messaging.v1.services.create({
        friendlyName: friendlyName,
      });

      logger.info({ sid: service.sid }, 'Messaging Service created');
      return { sid: service.sid };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to create Messaging Service');
      throw new Error(`Failed to create Messaging Service: ${error.message}`);
    }
  }

  /**
   * Assigns phone numbers to a Messaging Service
   */
  async assignPhoneNumbersToService(
    messagingServiceSid: string,
    phoneNumbers: string[]
  ): Promise<void> {
    try {
      logger.info({ messagingServiceSid, count: phoneNumbers.length }, 'Assigning phone numbers to service');

      for (const phoneNumber of phoneNumbers) {
        await this.client.messaging.v1
          .services(messagingServiceSid)
          .phoneNumbers.create({ phoneNumberSid: phoneNumber });
      }

      logger.info('Phone numbers assigned successfully');
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to assign phone numbers');
      throw new Error(`Failed to assign phone numbers: ${error.message}`);
    }
  }

  // ============================================================
  // STEP 11: CREATE A2P CAMPAIGN
  // ============================================================

  /**
   * Creates a US A2P Campaign
   */
  async createUsAppToPersonCampaign(
    brandSid: string,
    messagingServiceSid: string,
    campaign: CampaignInfo,
    messageFlow: string,
    optInMessage: string,
    optOutMessage: string,
    helpMessage: string
  ): Promise<{ sid: string; status: string }> {
    try {
      logger.info({ brandSid, messagingServiceSid, useCase: campaign.useCase }, 'Creating A2P Campaign');

      const a2pCampaign = await this.client.messaging.v1.a2p.brandRegistrations(brandSid)
        .usAppToPerson.create({
          messagingServiceSid: messagingServiceSid,
          description: campaign.description,
          messageFlow: messageFlow,
          usAppToPersonUsecase: campaign.useCase,
          hasEmbeddedLinks: campaign.hasEmbeddedLinks,
          hasEmbeddedPhone: campaign.hasEmbeddedPhone,
          subscriberOptIn: campaign.optInType === 'WEB_FORM',
          ageGated: false, // Can be parameterized if needed
          directLending: false,
          subscriberOptOut: true, // Always true
          subscriberHelp: true,   // Always true
          affiliateMarketing: false, // Can be parameterized
          messageVolume: '10000', // Can be parameterized based on LOW_VOLUME vs others
        });

      logger.info({ sid: a2pCampaign.sid, status: a2pCampaign.status }, 'A2P Campaign created');

      return {
        sid: a2pCampaign.sid,
        status: a2pCampaign.status,
      };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to create A2P Campaign');
      throw new Error(`Failed to create A2P Campaign: ${error.message}`);
    }
  }

  /**
   * Gets the status of a Campaign
   */
  async getCampaignStatus(
    brandSid: string,
    campaignSid: string
  ): Promise<{ status: string }> {
    try {
      const campaign = await this.client.messaging.v1.a2p
        .brandRegistrations(brandSid)
        .usAppToPerson(campaignSid)
        .fetch();

      return { status: campaign.status };
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to fetch campaign status');
      throw new Error(`Failed to fetch campaign status: ${error.message}`);
    }
  }

  // ============================================================
  // STEP 12: ASSIGN PHONE NUMBERS TO CAMPAIGN
  // ============================================================

  /**
   * Assigns phone numbers to a campaign
   * Note: This is typically automatic when using a Messaging Service,
   * but can be done explicitly if needed
   */
  async assignPhoneNumbersToCampaign(
    messagingServiceSid: string,
    phoneNumbers: string[]
  ): Promise<void> {
    // Phone numbers assigned to the Messaging Service are automatically
    // associated with campaigns using that service.
    // This is a no-op in most cases but kept for explicit control if needed.
    logger.info(
      { messagingServiceSid, count: phoneNumbers.length },
      'Phone numbers will be auto-associated via Messaging Service'
    );
  }

  // ============================================================
  // UTILITY METHODS
  // ============================================================

  /**
   * Checks if a CustomerProfile is approved
   */
  async isCustomerProfileApproved(customerProfileSid: string): Promise<boolean> {
    try {
      const profile = await this.client.trusthub.v1
        .customerProfiles(customerProfileSid)
        .fetch();
      
      return profile.status === 'twilio-approved';
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to check CustomerProfile status');
      return false;
    }
  }

  /**
   * Checks if a TrustProduct is approved
   */
  async isTrustProductApproved(trustProductSid: string): Promise<boolean> {
    try {
      const trustProduct = await this.client.trusthub.v1
        .trustProducts(trustProductSid)
        .fetch();
      
      return trustProduct.status === 'twilio-approved';
    } catch (error: any) {
      logger.error({ error: error.message }, 'Failed to check TrustProduct status');
      return false;
    }
  }
}
