/**
 * validators.ts — Input validation using Zod
 * Validates all input data BEFORE submitting to Twilio.
 */

import { z } from 'zod';
import type {
  BusinessType,
  BusinessIndustry,
  RegistrationIdentifier,
  RegionOfOperation,
  JobPosition,
  CampaignUseCase,
  OptInType,
} from '../types';

// ============================================================
// ENUM SCHEMAS
// ============================================================

const businessTypeSchema = z.enum([
  'Co-operative',
  'Corporation',
  'Limited Liability Corporation',
  'Non-profit Corporation',
  'Partnership',
]);

const businessIndustrySchema = z.enum([
  'AGRICULTURE', 'AUTOMOTIVE', 'BANKING', 'CONSTRUCTION',
  'CONSUMER', 'EDUCATION', 'ELECTRONICS', 'ENGINEERING',
  'ENERGY', 'FAST_MOVING_CONSUMER_GOODS', 'FINANCIAL',
  'FINTECH', 'FOOD_AND_BEVERAGE', 'GOVERNMENT', 'HEALTHCARE',
  'HOSPITALITY', 'INSURANCE', 'JEWELRY', 'LEGAL',
  'MANUFACTURING', 'MEDIA', 'NOT_FOR_PROFIT', 'OIL_AND_GAS',
  'ONLINE', 'PROFESSIONAL_SERVICES', 'RAW_MATERIALS',
  'REAL_ESTATE', 'RELIGION', 'RETAIL', 'TECHNOLOGY',
  'TELECOMMUNICATIONS', 'TRANSPORTATION', 'TRAVEL',
]);

const registrationIdentifierSchema = z.enum([
  'EIN', 'DUNS', 'CBN', 'CN', 'ACN', 'CIN',
  'VAT', 'VATRN', 'RN', 'Other',
]);

const regionOfOperationSchema = z.enum([
  'AFRICA', 'ASIA', 'EUROPE', 'LATIN_AMERICA', 'USA_AND_CANADA',
]);

const jobPositionSchema = z.enum([
  'Director', 'GM', 'VP', 'CEO', 'CFO', 'General Counsel', 'Other',
]);

const campaignUseCaseSchema = z.enum([
  'ACCOUNT_NOTIFICATION', 'CUSTOMER_CARE', 'DELIVERY_NOTIFICATION',
  'FRAUD_ALERT', 'HIGHER_EDUCATION', 'LOW_VOLUME',
  'MARKETING', 'MIXED', 'POLITICAL', 'POLLING_VOTING',
  'PUBLIC_SERVICE_ANNOUNCEMENT', 'SECURITY_ALERT',
]);

const optInTypeSchema = z.enum([
  'VERBAL', 'WEB_FORM', 'PAPER_FORM', 'VIA_TEXT',
  'MOBILE_QR_CODE', 'OTHER',
]);

// ============================================================
// FIELD VALIDATORS
// ============================================================

/**
 * Validates E.164 phone number format
 * Examples: +12125551234, +442071234567
 */
const e164PhoneSchema = z.string()
  .regex(/^\+[1-9]\d{1,14}$/, 'Phone number must be in E.164 format (e.g., +12125551234)');

/**
 * Validates EIN format (XX-XXXXXXX)
 */
const einSchema = z.string()
  .regex(/^\d{2}-\d{7}$/, 'EIN must be in format XX-XXXXXXX');

/**
 * Validates URL format
 */
const urlSchema = z.string()
  .url('Must be a valid URL');

/**
 * Validates email format
 */
const emailSchema = z.string()
  .email('Must be a valid email address');

/**
 * Validates 2-letter ISO country code
 */
const isoCountrySchema = z.string()
  .length(2, 'Country code must be 2 letters')
  .toUpperCase();

/**
 * Validates US state code (2 letters)
 */
const stateCodeSchema = z.string()
  .length(2, 'State/region code must be 2 letters')
  .toUpperCase();

// ============================================================
// BUSINESS INFO SCHEMA
// ============================================================

export const businessInfoSchema = z.object({
  businessName: z.string().min(1, 'Business name is required'),
  businessType: businessTypeSchema,
  businessIndustry: businessIndustrySchema,
  registrationIdentifier: registrationIdentifierSchema,
  registrationNumber: z.string().min(1, 'Registration number is required'),
  websiteUrl: urlSchema,
  socialMediaUrls: z.array(urlSchema).optional(),
  businessIdentity: z.enum(['direct_customer', 'isv_reseller_or_partner']),
  regionsOfOperation: z.array(regionOfOperationSchema).min(1, 'At least one region of operation required'),
  companyType: z.enum(['public', 'private', 'non-profit', 'government']),
  stockExchange: z.string().optional(),
  stockTicker: z.string().optional(),
  brandContactEmail: emailSchema.optional(),
  skipAutoSecVet: z.boolean().optional(),
}).refine(
  (data) => {
    // EIN validation if using EIN
    if (data.registrationIdentifier === 'EIN') {
      return /^\d{2}-\d{7}$/.test(data.registrationNumber);
    }
    return true;
  },
  {
    message: 'EIN must be in format XX-XXXXXXX',
    path: ['registrationNumber'],
  }
).refine(
  (data) => {
    // Public companies must provide stock exchange and ticker
    if (data.companyType === 'public') {
      return data.stockExchange && data.stockTicker;
    }
    return true;
  },
  {
    message: 'Public companies must provide stock exchange and ticker',
    path: ['stockExchange'],
  }
).refine(
  (data) => {
    // Public companies must provide brand contact email for 2FA
    if (data.companyType === 'public') {
      return data.brandContactEmail;
    }
    return true;
  },
  {
    message: 'Public companies must provide brand contact email',
    path: ['brandContactEmail'],
  }
);

// ============================================================
// AUTHORIZED REP SCHEMA
// ============================================================

export const authorizedRepSchema = z.object({
  firstName: z.string().min(1, 'First name is required'),
  lastName: z.string().min(1, 'Last name is required'),
  businessTitle: z.string().min(1, 'Business title is required'),
  jobPosition: jobPositionSchema,
  phoneNumber: e164PhoneSchema,
  email: emailSchema,
});

// ============================================================
// BUSINESS ADDRESS SCHEMA
// ============================================================

export const businessAddressSchema = z.object({
  customerName: z.string().min(1, 'Customer name is required'),
  street: z.string().min(1, 'Street address is required'),
  streetSecondary: z.string().optional(),
  city: z.string().min(1, 'City is required'),
  region: stateCodeSchema,
  postalCode: z.string().min(1, 'Postal code is required'),
  isoCountry: isoCountrySchema,
});

// ============================================================
// CAMPAIGN INFO SCHEMA
// ============================================================

export const campaignInfoSchema = z.object({
  useCase: campaignUseCaseSchema,
  description: z.string()
    .min(40, 'Campaign description must be at least 40 characters')
    .max(4096, 'Campaign description must be less than 4096 characters'),
  sampleMessages: z.array(z.string())
    .min(1, 'At least one sample message is required')
    .max(5, 'Maximum 5 sample messages allowed'),
  messageFlow: z.string()
    .min(40, 'Message flow must be at least 40 characters')
    .max(2048, 'Message flow must be less than 2048 characters'),
  optInType: optInTypeSchema,
  optInMessage: z.string()
    .min(20, 'Opt-in message must be at least 20 characters')
    .max(320, 'Opt-in message must be less than 320 characters'),
  optOutMessage: z.string()
    .min(20, 'Opt-out message must be at least 20 characters')
    .max(320, 'Opt-out message must be less than 320 characters'),
  helpMessage: z.string()
    .min(20, 'Help message must be at least 20 characters')
    .max(320, 'Help message must be less than 320 characters'),
  optInKeywords: z.array(z.string()).optional(),
  optOutKeywords: z.array(z.string()).optional(),
  helpKeywords: z.array(z.string()).optional(),
  hasEmbeddedLinks: z.boolean(),
  hasEmbeddedPhone: z.boolean(),
});

// ============================================================
// PHONE CONFIG SCHEMA
// ============================================================

export const phoneConfigSchema = z.object({
  messagingServiceSid: z.string().regex(/^MG[a-f0-9]{32}$/, 'Invalid Messaging Service SID').optional(),
  phoneNumbers: z.array(e164PhoneSchema).optional(),
}).refine(
  (data) => {
    // Must provide either messaging service SID or phone numbers
    return data.messagingServiceSid || (data.phoneNumbers && data.phoneNumbers.length > 0);
  },
  {
    message: 'Must provide either messagingServiceSid or phoneNumbers',
  }
);

// ============================================================
// FULL REGISTRATION INPUT SCHEMA
// ============================================================

export const registrationInputSchema = z.object({
  business: businessInfoSchema,
  authorizedRep: authorizedRepSchema,
  address: businessAddressSchema,
  campaign: campaignInfoSchema,
  phone: phoneConfigSchema,
  externalId: z.string().optional(),
  notifyWebhook: urlSchema.optional(),
  notifyEmail: emailSchema.optional(),
});

// ============================================================
// VALIDATION HELPER FUNCTIONS
// ============================================================

/**
 * Validates registration input and returns formatted errors
 */
export function validateRegistrationInput(input: unknown): {
  success: boolean;
  errors?: Record<string, string[]>;
  data?: any;
} {
  const result = registrationInputSchema.safeParse(input);
  
  if (result.success) {
    return { success: true, data: result.data };
  }
  
  // Format Zod errors into a readable structure
  const errors: Record<string, string[]> = {};
  
  for (const issue of result.error.issues) {
    const path = issue.path.join('.');
    if (!errors[path]) {
      errors[path] = [];
    }
    errors[path].push(issue.message);
  }
  
  return { success: false, errors };
}

/**
 * Validates a specific field against its schema
 */
export function validateField(fieldName: string, value: unknown): {
  success: boolean;
  error?: string;
} {
  const schemas: Record<string, z.ZodType> = {
    phone: e164PhoneSchema,
    email: emailSchema,
    url: urlSchema,
    ein: einSchema,
    isoCountry: isoCountrySchema,
    stateCode: stateCodeSchema,
  };
  
  const schema = schemas[fieldName];
  if (!schema) {
    return { success: false, error: `Unknown field: ${fieldName}` };
  }
  
  const result = schema.safeParse(value);
  if (result.success) {
    return { success: true };
  }
  
  return { success: false, error: result.error.issues[0].message };
}

/**
 * Checks if a string is a valid Twilio SID format
 */
export function isValidTwilioSid(sid: string, prefix: string): boolean {
  const regex = new RegExp(`^${prefix}[a-f0-9]{32}$`);
  return regex.test(sid);
}

/**
 * Validates SID chain completeness for a specific step
 */
export function validateSidChainForStep(sidChain: any, step: number): {
  valid: boolean;
  missing?: string[];
} {
  const missing: string[] = [];
  
  // Define required SIDs per step
  const requirements: Record<number, string[]> = {
    1: [],
    2: ['customerProfileSid'],
    3: ['customerProfileSid', 'businessEndUserSid'],
    4: ['customerProfileSid', 'businessEndUserSid', 'authorizedRepEndUserSid'],
    5: ['customerProfileSid', 'businessEndUserSid', 'authorizedRepEndUserSid', 'addressSid', 'supportingDocSid'],
    6: ['customerProfileSid'],
    7: ['customerProfileSid'],
    8: ['customerProfileSid'],
    9: ['trustProductSid', 'a2pProfileEndUserSid'],
    10: ['brandRegistrationSid'],
    11: ['brandRegistrationSid', 'messagingServiceSid'],
    12: ['campaignSid'],
  };
  
  const required = requirements[step] || [];
  
  for (const field of required) {
    if (!sidChain[field]) {
      missing.push(field);
    }
  }
  
  return {
    valid: missing.length === 0,
    missing: missing.length > 0 ? missing : undefined,
  };
}
