import Twilio from 'twilio';

let _client: Twilio.Twilio | null = null;

export function getTwilioClient(): Twilio.Twilio {
  if (!_client) {
    const sid = process.env.TWILIO_ACCOUNT_SID;
    const token = process.env.TWILIO_AUTH_TOKEN;
    if (!sid || !token) throw new Error('TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN required');
    _client = Twilio(sid, token);
  }
  return _client;
}

export function hasTwilioCredentials(): boolean {
  return !!(process.env.TWILIO_ACCOUNT_SID && process.env.TWILIO_AUTH_TOKEN);
}

export async function sendSms(to: string, from: string, body: string): Promise<string> {
  const client = getTwilioClient();
  const msg = await client.messages.create({ to, from, body });
  return msg.sid;
}

export async function testConnection(): Promise<boolean> {
  try {
    const client = getTwilioClient();
    await client.api.accounts(process.env.TWILIO_ACCOUNT_SID!).fetch();
    return true;
  } catch {
    return false;
  }
}

export interface OwnedNumber {
  sid: string;
  phoneNumber: string;
  friendlyName: string;
  smsEnabled: boolean;
  voiceEnabled: boolean;
  mmsEnabled: boolean;
  dateCreated: string;
  smsUrl: string | null;
  voiceUrl: string | null;
}

export async function listPhoneNumbers(): Promise<Array<{ sid: string; phoneNumber: string; friendlyName: string }>> {
  try {
    const client = getTwilioClient();
    const numbers = await client.incomingPhoneNumbers.list({ limit: 20 });
    return numbers.map(n => ({
      sid: n.sid,
      phoneNumber: n.phoneNumber,
      friendlyName: n.friendlyName,
    }));
  } catch {
    return [];
  }
}

export async function listPhoneNumbersDetailed(): Promise<OwnedNumber[]> {
  try {
    const client = getTwilioClient();
    const numbers = await client.incomingPhoneNumbers.list({ limit: 50 });
    return numbers.map(n => ({
      sid: n.sid,
      phoneNumber: n.phoneNumber,
      friendlyName: n.friendlyName,
      smsEnabled: n.capabilities?.sms ?? false,
      voiceEnabled: n.capabilities?.voice ?? false,
      mmsEnabled: n.capabilities?.mms ?? false,
      dateCreated: n.dateCreated?.toISOString() ?? '',
      smsUrl: n.smsUrl ?? null,
      voiceUrl: n.voiceUrl ?? null,
    }));
  } catch {
    return [];
  }
}

export interface AvailableNumber {
  phoneNumber: string;
  friendlyName: string;
  locality: string;
  region: string;
  isoCountry: string;
  capabilities: {
    sms: boolean;
    voice: boolean;
    mms: boolean;
  };
}

export interface SearchNumbersOptions {
  country?: string;
  areaCode?: string;
  contains?: string;
  smsEnabled?: boolean;
  voiceEnabled?: boolean;
  mmsEnabled?: boolean;
  type?: 'local' | 'tollFree';
  limit?: number;
}

export async function searchAvailableNumbers(opts: SearchNumbersOptions): Promise<AvailableNumber[]> {
  const client = getTwilioClient();
  const country = opts.country || 'US';
  const limit = opts.limit || 20;

  const searchParams: Record<string, unknown> = { limit };
  if (opts.areaCode) searchParams.areaCode = parseInt(opts.areaCode, 10);
  if (opts.contains) searchParams.contains = opts.contains;
  if (opts.smsEnabled !== undefined) searchParams.smsEnabled = opts.smsEnabled;
  if (opts.voiceEnabled !== undefined) searchParams.voiceEnabled = opts.voiceEnabled;
  if (opts.mmsEnabled !== undefined) searchParams.mmsEnabled = opts.mmsEnabled;

  /* eslint-disable @typescript-eslint/no-explicit-any */
  let numbers: any[];
  if (opts.type === 'tollFree') {
    numbers = await client.availablePhoneNumbers(country).tollFree.list(searchParams as any);
  } else {
    numbers = await client.availablePhoneNumbers(country).local.list(searchParams as any);
  }
  /* eslint-enable @typescript-eslint/no-explicit-any */

  return numbers.map((n) => ({
    phoneNumber: n.phoneNumber,
    friendlyName: n.friendlyName,
    locality: n.locality ?? '',
    region: n.region ?? '',
    isoCountry: n.isoCountry ?? country,
    capabilities: {
      sms: n.capabilities?.sms ?? false,
      voice: n.capabilities?.voice ?? false,
      mms: n.capabilities?.mms ?? false,
    },
  }));
}

export async function buyPhoneNumber(
  phoneNumber: string,
  friendlyName?: string
): Promise<OwnedNumber> {
  const client = getTwilioClient();
  const params: { phoneNumber: string; friendlyName?: string } = { phoneNumber };
  if (friendlyName) params.friendlyName = friendlyName;
  const n = await client.incomingPhoneNumbers.create(params);
  return {
    sid: n.sid,
    phoneNumber: n.phoneNumber,
    friendlyName: n.friendlyName,
    smsEnabled: n.capabilities?.sms ?? false,
    voiceEnabled: n.capabilities?.voice ?? false,
    mmsEnabled: n.capabilities?.mms ?? false,
    dateCreated: n.dateCreated?.toISOString() ?? '',
    smsUrl: n.smsUrl ?? null,
    voiceUrl: n.voiceUrl ?? null,
  };
}

export async function releasePhoneNumber(sid: string): Promise<boolean> {
  const client = getTwilioClient();
  return await client.incomingPhoneNumbers(sid).remove();
}

export async function updatePhoneNumber(
  sid: string,
  updates: { friendlyName?: string; smsUrl?: string; voiceUrl?: string }
): Promise<OwnedNumber> {
  const client = getTwilioClient();
  const n = await client.incomingPhoneNumbers(sid).update(updates);
  return {
    sid: n.sid,
    phoneNumber: n.phoneNumber,
    friendlyName: n.friendlyName,
    smsEnabled: n.capabilities?.sms ?? false,
    voiceEnabled: n.capabilities?.voice ?? false,
    mmsEnabled: n.capabilities?.mms ?? false,
    dateCreated: n.dateCreated?.toISOString() ?? '',
    smsUrl: n.smsUrl ?? null,
    voiceUrl: n.voiceUrl ?? null,
  };
}
