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

export class NumbersPack extends BasePack {
  readonly name = 'numbers';
  readonly description = 'Search, buy, configure, and manage phone numbers';
  readonly keywords = ['phone', 'number', 'buy', 'provision', 'release', 'inventory', 'sms', 'voice'];

  setup(): void {
    // ─── twilio_number_hunt ───
    this.registerTool({
      name: 'twilio_number_hunt',
      description: 'Search for available phone numbers to purchase. Filter by country, type, area code, pattern, city, capabilities, and more.',
      category: 'numbers',
      safety: 'green',
      tier: 2,
      isApp: true,
      annotations: { readOnlyHint: true },
      inputSchema: z.object({
        country: z.string().default('US').describe('2-letter country code (e.g., US, GB, CA)'),
        type: z.enum(['local', 'tollFree', 'mobile']).default('local').describe('Number type'),
        area_code: z.string().optional().describe('Area code to search within'),
        contains: z.string().optional().describe('Pattern the number should contain (e.g., *FOOD*, **1234)'),
        city: z.string().optional().describe('City name'),
        state: z.string().optional().describe('State/region code (e.g., CA, NY)'),
        sms_enabled: z.boolean().optional().describe('Must support SMS'),
        voice_enabled: z.boolean().optional().describe('Must support voice'),
        limit: z.number().default(10).describe('Maximum results to return'),
      }),
      _meta: {
        labels: {
          category: "phone-numbers",
          access: "read",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        try {
          // Build search params
          const searchParams: any = {};
          if (params.area_code) searchParams.areaCode = params.area_code;
          if (params.contains) searchParams.contains = params.contains;
          if (params.city) searchParams.inLocality = params.city;
          if (params.state) searchParams.inRegion = params.state;
          if (params.sms_enabled !== undefined) searchParams.smsEnabled = params.sms_enabled;
          if (params.voice_enabled !== undefined) searchParams.voiceEnabled = params.voice_enabled;

          // Execute search based on type
          let results: any[] = [];
          
          if (params.type === 'local') {
            results = await this.client.execute(c =>
              c.availablePhoneNumbers(params.country).local.list({ ...searchParams, limit: params.limit })
            );
          } else if (params.type === 'tollFree') {
            results = await this.client.execute(c =>
              c.availablePhoneNumbers(params.country).tollFree.list({ ...searchParams, limit: params.limit })
            );
          } else if (params.type === 'mobile') {
            results = await this.client.execute(c =>
              c.availablePhoneNumbers(params.country).mobile.list({ ...searchParams, limit: params.limit })
            );
          }

          if (results.length === 0) {
            return this.textResult('No available numbers found matching your criteria.');
          }

          // Build HTML table
          const rows = results.map(n => [
            `<strong>${renderer.formatPhone(n.phoneNumber)}</strong>`,
            n.locality && n.region ? `${n.locality}, ${n.region}` : (n.locality || n.region || '—'),
            [
              n.capabilities?.sms ? renderer.badge('SMS', 'success') : '',
              n.capabilities?.voice ? renderer.badge('Voice', 'info') : '',
              n.capabilities?.mms ? renderer.badge('MMS', 'success') : '',
            ].filter(Boolean).join(' ') || '—',
            n.addressRequirements === 'none' ? '—' : renderer.badge(n.addressRequirements, 'warning'),
          ]);

          const html = renderer.appLayout(
            '🔍 Available Phone Numbers',
            `Found ${results.length} ${params.type} number${results.length === 1 ? '' : 's'} in ${params.country}`,
            `
            <div class="card">
              ${renderer.table(
                ['Number', 'Location', 'Capabilities', 'Address Req'],
                rows
              )}
            </div>
            <div class="muted small" style="margin-top: 8px;">
              To purchase a number, use <strong>twilio_number_provision</strong> with the phone number.
            </div>
            `
          );

          return this.appResult(
            html,
            `Found ${results.length} available ${params.type} numbers in ${params.country}:\n` +
            results.map(n => `  ${n.phoneNumber} (${n.locality || n.region || 'N/A'})`).join('\n')
          );
        } catch (err: any) {
          return this.errorResult(`Number search failed: ${err.message}`);
        }
      },
    });

    // ─── twilio_number_provision ───
    this.registerTool({
      name: 'twilio_number_provision',
      description: 'Purchase a specific phone number. Optionally configure webhooks and messaging service on purchase.',
      category: 'numbers',
      safety: 'red',
      tier: 2,
      annotations: { costHint: true, estimatedCost: 'Varies by number type ($1-15/month typical)' },
      inputSchema: z.object({
        phone_number: z.string().describe('Specific phone number to purchase (E.164 format)'),
        friendly_name: z.string().optional().describe('Human-readable name for this number'),
        sms_url: z.string().optional().describe('Webhook URL for incoming SMS'),
        voice_url: z.string().optional().describe('Webhook URL for incoming calls'),
        messaging_service_sid: z.string().optional().describe('Messaging Service SID to attach this number to'),
      }),
      _meta: {
        labels: {
          category: "phone-numbers",
          access: "delete",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        const phoneNumber = toE164(params.phone_number);
        if (!isValidPhone(phoneNumber)) {
          return this.errorResult(`Invalid phone number: ${params.phone_number}`);
        }

        try {
          // Purchase the number
          const createParams: any = { phoneNumber };
          if (params.friendly_name) createParams.friendlyName = params.friendly_name;
          if (params.sms_url) createParams.smsUrl = params.sms_url;
          if (params.voice_url) createParams.voiceUrl = params.voice_url;

          const number = await this.client.execute(c =>
            c.incomingPhoneNumbers.create(createParams)
          );

          // Attach to messaging service if requested
          if (params.messaging_service_sid) {
            await this.client.execute(c =>
              c.incomingPhoneNumbers(number.sid).update({
                smsApplicationSid: params.messaging_service_sid,
              } as any)
            );
          }

          const capabilities = [
            number.capabilities?.sms ? '✓ SMS' : '',
            number.capabilities?.voice ? '✓ Voice' : '',
            number.capabilities?.mms ? '✓ MMS' : '',
          ].filter(Boolean).join(' | ');

          return this.textResult(
            `✅ Phone number provisioned!\n\n` +
            `  Number: ${number.phoneNumber}\n` +
            `  SID: ${number.sid}\n` +
            `  Name: ${number.friendlyName || '(none)'}\n` +
            `  Capabilities: ${capabilities}\n` +
            `  SMS URL: ${number.smsUrl || '(none)'}\n` +
            `  Voice URL: ${number.voiceUrl || '(none)'}\n` +
            `  Messaging Service: ${params.messaging_service_sid || '(none)'}\n\n` +
            `This number is now active and ready to use.`
          );
        } catch (err: any) {
          return this.errorResult(`Failed to provision number: ${err.message}`);
        }
      },
    });

    // ─── twilio_number_inventory ───
    this.registerTool({
      name: 'twilio_number_inventory',
      description: 'List all phone numbers owned by this account with configuration details and capabilities.',
      category: 'numbers',
      safety: 'green',
      tier: 2,
      isApp: true,
      annotations: { readOnlyHint: true },
      inputSchema: z.object({
        filter: z.string().optional().describe('Search by phone number or friendly name'),
        capabilities: z.enum(['sms', 'voice', 'mms']).optional().describe('Filter by capability'),
      }),
      _meta: {
        labels: {
          category: "phone-numbers",
          access: "read",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        try {
          let numbers = await this.client.execute(c =>
            c.incomingPhoneNumbers.list({ limit: 100 })
          );

          // Apply filters
          if (params.filter) {
            const filterLower = params.filter.toLowerCase();
            numbers = numbers.filter(n =>
              n.phoneNumber.includes(params.filter!) ||
              (n.friendlyName && n.friendlyName.toLowerCase().includes(filterLower))
            );
          }

          if (params.capabilities) {
            numbers = numbers.filter(n => {
              switch (params.capabilities) {
                case 'sms': return n.capabilities?.sms;
                case 'voice': return n.capabilities?.voice;
                case 'mms': return n.capabilities?.mms;
                default: return true;
              }
            });
          }

          if (numbers.length === 0) {
            return this.textResult('No phone numbers found matching your criteria.');
          }

          // Build HTML table
          const rows = numbers.map(n => {
            const caps = [
              n.capabilities?.sms ? renderer.badge('SMS', 'success') : '',
              n.capabilities?.voice ? renderer.badge('Voice', 'info') : '',
              n.capabilities?.mms ? renderer.badge('MMS', 'success') : '',
            ].filter(Boolean).join(' ');

            return [
              `<strong>${renderer.formatPhone(n.phoneNumber)}</strong>`,
              n.friendlyName || '—',
              caps || '—',
              n.smsUrl ? `<span class="small">${n.smsUrl.substring(0, 40)}…</span>` : '—',
              n.voiceUrl ? `<span class="small">${n.voiceUrl.substring(0, 40)}…</span>` : '—',
              `<span class="small muted">${new Date(n.dateCreated).toLocaleDateString()}</span>`,
            ];
          });

          const html = renderer.appLayout(
            '📱 Phone Number Inventory',
            `${numbers.length} number${numbers.length === 1 ? '' : 's'} in your account`,
            `
            <div class="card">
              ${renderer.table(
                ['Number', 'Friendly Name', 'Capabilities', 'SMS URL', 'Voice URL', 'Date Created'],
                rows
              )}
            </div>
            `
          );

          return this.appResult(
            html,
            `Your phone numbers (${numbers.length}):\n` +
            numbers.map(n => `  ${n.phoneNumber} — ${n.friendlyName || '(unnamed)'}`).join('\n')
          );
        } catch (err: any) {
          return this.errorResult(`Failed to fetch inventory: ${err.message}`);
        }
      },
    });

    // ─── twilio_number_configure ───
    this.registerTool({
      name: 'twilio_number_configure',
      description: 'Update configuration for an existing phone number — webhooks, friendly name, HTTP methods.',
      category: 'numbers',
      safety: 'yellow',
      tier: 2,
      inputSchema: z.object({
        phone_number: z.string().describe('Phone number or SID to configure'),
        friendly_name: z.string().optional().describe('New friendly name'),
        sms_url: z.string().optional().describe('Webhook URL for incoming SMS'),
        sms_method: z.enum(['GET', 'POST']).optional().describe('HTTP method for SMS webhook'),
        voice_url: z.string().optional().describe('Webhook URL for incoming calls'),
        voice_method: z.enum(['GET', 'POST']).optional().describe('HTTP method for voice webhook'),
        status_callback: z.string().optional().describe('Status callback URL'),
      }),
      _meta: {
        labels: {
          category: "phone-numbers",
          access: "write",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        try {
          // Find the number by phone or SID
          let numberSid: string;
          if (isSid(params.phone_number, 'PN')) {
            numberSid = params.phone_number;
          } else {
            const phone = toE164(params.phone_number);
            const numbers = await this.client.execute(c =>
              c.incomingPhoneNumbers.list({ phoneNumber: phone, limit: 1 })
            );
            if (numbers.length === 0) {
              return this.errorResult(`Phone number not found: ${params.phone_number}`);
            }
            numberSid = numbers[0].sid;
          }

          // Build update params
          const updateParams: any = {};
          if (params.friendly_name) updateParams.friendlyName = params.friendly_name;
          if (params.sms_url) updateParams.smsUrl = params.sms_url;
          if (params.sms_method) updateParams.smsMethod = params.sms_method;
          if (params.voice_url) updateParams.voiceUrl = params.voice_url;
          if (params.voice_method) updateParams.voiceMethod = params.voice_method;
          if (params.status_callback) updateParams.statusCallback = params.status_callback;

          const updated = await this.client.execute(c =>
            c.incomingPhoneNumbers(numberSid).update(updateParams)
          );

          return this.textResult(
            `✅ Number configuration updated!\n\n` +
            `  Number: ${updated.phoneNumber}\n` +
            `  Name: ${updated.friendlyName || '(none)'}\n` +
            `  SMS URL: ${updated.smsUrl || '(none)'}\n` +
            `  SMS Method: ${updated.smsMethod || 'POST'}\n` +
            `  Voice URL: ${updated.voiceUrl || '(none)'}\n` +
            `  Voice Method: ${updated.voiceMethod || 'POST'}\n` +
            `  Status Callback: ${updated.statusCallback || '(none)'}`
          );
        } catch (err: any) {
          return this.errorResult(`Configuration update failed: ${err.message}`);
        }
      },
    });

    // ─── twilio_number_release ───
    this.registerTool({
      name: 'twilio_number_release',
      description: 'Release (delete) a phone number from your account. This action is permanent and cannot be undone. The number becomes available for others to purchase.',
      category: 'numbers',
      safety: 'red',
      tier: 2,
      annotations: { destructiveHint: true },
      inputSchema: z.object({
        phone_number: z.string().describe('Phone number or SID to release'),
        confirm: z.boolean().describe('Must be true to confirm release'),
      }),
      _meta: {
        labels: {
          category: "phone-numbers",
          access: "delete",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        if (!params.confirm) {
          return this.errorResult('You must set confirm=true to release a phone number. This action is permanent.');
        }

        try {
          // Find the number by phone or SID
          let number: any;
          if (isSid(params.phone_number, 'PN')) {
            number = await this.client.execute(c =>
              c.incomingPhoneNumbers(params.phone_number).fetch()
            );
          } else {
            const phone = toE164(params.phone_number);
            const numbers = await this.client.execute(c =>
              c.incomingPhoneNumbers.list({ phoneNumber: phone, limit: 1 })
            );
            if (numbers.length === 0) {
              return this.errorResult(`Phone number not found: ${params.phone_number}`);
            }
            number = numbers[0];
          }

          // Warn about active configurations
          const warnings: string[] = [];
          if (number.smsUrl) warnings.push('Active SMS webhook configured');
          if (number.voiceUrl) warnings.push('Active voice webhook configured');
          if (number.messagingServiceSid) warnings.push('Attached to messaging service');

          // Release the number
          await this.client.execute(c =>
            c.incomingPhoneNumbers(number.sid).remove()
          );

          let warningText = '';
          if (warnings.length > 0) {
            warningText = `\n\n⚠️  This number had active configurations:\n  ${warnings.join('\n  ')}`;
          }

          return this.textResult(
            `✅ Phone number released!\n\n` +
            `  Number: ${number.phoneNumber}\n` +
            `  Name: ${number.friendlyName || '(unnamed)'}\n` +
            `  SID: ${number.sid}\n\n` +
            `This number has been released and is no longer active on your account.${warningText}`
          );
        } catch (err: any) {
          return this.errorResult(`Failed to release number: ${err.message}`);
        }
      },
    });
  }
}
