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

export class Tier1Pack extends BasePack {
  readonly name = 'tier1';
  readonly description = 'Always-loaded navigator tools';
  readonly keywords: string[] = [];
  private loader: LazyLoader;

  constructor(client: TwilioClient, loader: LazyLoader) {
    super(client);
    this.loader = loader;
  }

  setup(): void {
    // ─── twilio_explore ───
    this.registerTool({
      name: 'twilio_explore',
      description: 'Discover available Twilio capabilities and load tool packs on demand. Call with a category to load that pack, or without to see all available packs.',
      category: 'navigation',
      safety: 'green',
      tier: 1,
      annotations: { readOnlyHint: true },
      inputSchema: z.object({
        category: z.enum([
          'messaging', 'voice', 'numbers', 'intelligence', 'verify',
          'studio', 'contact-center', 'conversations', 'analytics',
          'serverless', 'compliance',
        ]).optional().describe('Category to explore and load. Omit to see all available packs.'),
      }),
      _meta: {
        labels: {
          category: "navigation",
          access: "read",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        if (params.category) {
          const result = this.loader.loadPack(params.category);
          if (!result.loaded) {
            return this.errorResult(`Unknown pack: ${params.category}`);
          }
          return this.textResult(
            `Loaded ${params.category} pack with ${result.tools.length} tools:\n` +
            result.tools.map(t => `  • ${t}`).join('\n') +
            '\n\nThese tools are now available for use.'
          );
        }

        const packs = this.loader.getAvailablePacks();
        const lines = packs.map(p =>
          `${p.loaded ? '✓' : '○'} **${p.name}** — ${p.description}${p.loaded ? ' (loaded)' : ''}`
        );
        return this.textResult(
          'Available Twilio tool packs:\n\n' +
          lines.join('\n') +
          '\n\nCall twilio_explore with a category to load that pack.'
        );
      },
    });

    // ─── twilio_dashboard ───
    this.registerTool({
      name: 'twilio_dashboard',
      description: 'Get a visual dashboard of your Twilio account — balance, usage, message stats, call stats, active numbers.',
      category: 'navigation',
      safety: 'green',
      tier: 1,
      isApp: true,
      annotations: { readOnlyHint: true },
      inputSchema: z.object({}),
      _meta: {
        labels: {
          category: "navigation",
          access: "read",
          complexity: "complex"
        }
      },
      handler: async () => {
        try {
          // Fetch account info
          const account = await this.client.execute(c =>
            c.api.accounts(this.client.accountSid).fetch()
          );

          // Fetch balance
          let balance = 'N/A';
          try {
            const bal: any = await this.client.execute(c =>
              (c as any).api.accounts(this.client.accountSid).balance.fetch()
            );
            balance = `$${parseFloat(bal.balance).toFixed(2)}`;
          } catch { /* balance API may not be available */ }

          // Fetch recent messages (last 20)
          const messages = await this.client.execute(c =>
            c.messages.list({ limit: 20 })
          );
          const sent = messages.filter(m => m.direction.startsWith('outbound')).length;
          const received = messages.filter(m => m.direction === 'inbound').length;
          const failed = messages.filter(m => m.status === 'failed' || m.status === 'undelivered').length;

          // Fetch recent calls (last 20)
          const calls = await this.client.execute(c =>
            c.calls.list({ limit: 20 })
          );
          const totalCallMinutes = calls.reduce((acc, c) => acc + (parseInt(c.duration || '0')), 0);

          // Fetch phone numbers
          const numbers = await this.client.execute(c =>
            c.incomingPhoneNumbers.list({ limit: 100 })
          );

          // Build the dashboard HTML
          const html = renderer.appLayout(
            '📊 Twilio Dashboard',
            `Account: ${account.friendlyName} (${this.client.accountSid})`,
            `
            <div class="card-grid card-grid-4">
              ${renderer.metricCard(balance, 'Balance')}
              ${renderer.metricCard(String(numbers.length), 'Phone Numbers')}
              ${renderer.metricCard(String(messages.length), 'Recent Messages')}
              ${renderer.metricCard(formatDuration(totalCallMinutes), 'Call Minutes')}
            </div>

            <div class="card-grid card-grid-2">
              <div class="card">
                <div class="card-header">📱 Message Activity (Last 20)</div>
                <div class="card-grid card-grid-3" style="margin-top: 8px;">
                  ${renderer.metricCard(String(sent), 'Sent')}
                  ${renderer.metricCard(String(received), 'Received')}
                  ${renderer.metricCard(String(failed), 'Failed')}
                </div>
              </div>
              <div class="card">
                <div class="card-header">📞 Call Activity (Last 20)</div>
                <div class="card-grid card-grid-3" style="margin-top: 8px;">
                  ${renderer.metricCard(String(calls.filter(c => c.status === 'completed').length), 'Completed')}
                  ${renderer.metricCard(String(calls.filter(c => c.status === 'no-answer').length), 'No Answer')}
                  ${renderer.metricCard(String(calls.filter(c => c.status === 'failed').length), 'Failed')}
                </div>
              </div>
            </div>

            <div class="card">
              <div class="card-header">📋 Phone Numbers</div>
              ${renderer.table(
                ['Number', 'Name', 'SMS', 'Voice', 'MMS'],
                numbers.slice(0, 10).map(n => [
                  `<strong>${n.phoneNumber}</strong>`,
                  n.friendlyName || '—',
                  n.capabilities?.sms ? renderer.badge('SMS', 'success') : renderer.badge('—', 'muted'),
                  n.capabilities?.voice ? renderer.badge('Voice', 'info') : renderer.badge('—', 'muted'),
                  n.capabilities?.mms ? renderer.badge('MMS', 'success') : renderer.badge('—', 'muted'),
                ])
              )}
              ${numbers.length > 10 ? `<div class="muted small" style="padding: 8px 12px;">+ ${numbers.length - 10} more numbers</div>` : ''}
            </div>
            `
          );

          return this.appResult(html,
            `Twilio Dashboard — Balance: ${balance}, Numbers: ${numbers.length}, ` +
            `Messages: ${sent} sent / ${received} received / ${failed} failed, ` +
            `Calls: ${calls.length} recent (${formatDuration(totalCallMinutes)} total)`
          );
        } catch (err: any) {
          return this.errorResult(`Failed to load dashboard: ${err.message}`);
        }
      },
    });

    // ─── twilio_quick_send ───
    this.registerTool({
      name: 'twilio_quick_send',
      description: 'Send an SMS/MMS/WhatsApp message. Auto-validates the number and picks the best sender.',
      category: 'messaging',
      safety: 'yellow',
      tier: 1,
      annotations: { costHint: true, estimatedCost: '~$0.0079/SMS (US)' },
      inputSchema: z.object({
        to: z.string().describe('Recipient phone number'),
        body: z.string().describe('Message text'),
        media_url: z.string().optional().describe('URL of media to attach (MMS)'),
        from: z.string().optional().describe('Sender phone number (auto-selected if omitted)'),
      }),
      _meta: {
        labels: {
          category: "messaging",
          access: "write",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        const to = toE164(params.to);
        if (!isValidPhone(to)) {
          return this.errorResult(`Invalid phone number: ${params.to}`);
        }

        try {
          // If no from number specified, find one
          let from = params.from;
          if (!from) {
            const numbers = await this.client.execute(c =>
              c.incomingPhoneNumbers.list({ limit: 1 })
            );
            if (numbers.length === 0) {
              return this.errorResult('No phone numbers on this account. Buy one first with twilio_number_hunt.');
            }
            from = numbers[0].phoneNumber;
          }

          const msgParams: any = {
            to,
            from: toE164(from),
            body: params.body,
          };
          if (params.media_url) {
            msgParams.mediaUrl = [params.media_url];
          }

          const message = await this.client.execute(c => c.messages.create(msgParams));

          const statusInfo = messageStatusInfo(message.status);
          return this.textResult(
            `Message sent!\n` +
            `  SID: ${message.sid}\n` +
            `  To: ${message.to}\n` +
            `  From: ${message.from}\n` +
            `  Status: ${statusInfo.label}\n` +
            `  Cost: ${message.price ? `$${Math.abs(parseFloat(message.price))}` : 'pending'}`
          );
        } catch (err: any) {
          return this.errorResult(`Failed to send message: ${err.message}`);
        }
      },
    });

    // ─── twilio_quick_call ───
    this.registerTool({
      name: 'twilio_quick_call',
      description: 'Place an outbound call. Say a message, connect to another number, or record.',
      category: 'voice',
      safety: 'yellow',
      tier: 1,
      annotations: { costHint: true, estimatedCost: '~$0.014/min (US)' },
      inputSchema: z.object({
        to: z.string().describe('Number to call'),
        action: z.enum(['say', 'connect', 'record']).describe('What to do when they answer'),
        message: z.string().optional().describe('Message to say (for action=say)'),
        connect_to: z.string().optional().describe('Number to connect to (for action=connect)'),
        voice: z.string().optional().describe('Voice to use (e.g., Polly.Amy, Polly.Matthew)'),
        from: z.string().optional().describe('Caller ID number (auto-selected if omitted)'),
      }),
      _meta: {
        labels: {
          category: "calls",
          access: "read",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        const to = toE164(params.to);
        if (!isValidPhone(to)) {
          return this.errorResult(`Invalid phone number: ${params.to}`);
        }

        try {
          let from = params.from;
          if (!from) {
            const numbers = await this.client.execute(c =>
              c.incomingPhoneNumbers.list({ limit: 1 })
            );
            if (numbers.length === 0) {
              return this.errorResult('No phone numbers on this account.');
            }
            from = numbers[0].phoneNumber;
          }

          const voice = params.voice || 'Polly.Amy';
          let twiml: string;

          switch (params.action) {
            case 'say':
              twiml = `<Response><Say voice="${voice}">${params.message || 'Hello!'}</Say></Response>`;
              break;
            case 'connect':
              if (!params.connect_to) return this.errorResult('connect_to is required for action=connect');
              twiml = `<Response><Say voice="${voice}">Connecting you now.</Say><Dial>${toE164(params.connect_to)}</Dial></Response>`;
              break;
            case 'record':
              twiml = `<Response><Say voice="${voice}">${params.message || 'Please leave a message after the beep.'}</Say><Record maxLength="120" transcribe="true"/></Response>`;
              break;
          }

          const call = await this.client.execute(c =>
            c.calls.create({ to, from: toE164(from!), twiml })
          );

          return this.textResult(
            `Call initiated!\n` +
            `  SID: ${call.sid}\n` +
            `  To: ${call.to}\n` +
            `  From: ${call.from}\n` +
            `  Status: ${call.status}\n` +
            `  Action: ${params.action}`
          );
        } catch (err: any) {
          return this.errorResult(`Failed to place call: ${err.message}`);
        }
      },
    });

    // ─── twilio_lookup ───
    this.registerTool({
      name: 'twilio_lookup',
      description: 'Get intelligence on any phone number — validation, carrier, line type, caller name. Three depth levels: basic (free), standard (line type + carrier), deep (everything).',
      category: 'intelligence',
      safety: 'green',
      tier: 1,
      annotations: { readOnlyHint: true, costHint: true, estimatedCost: 'Free (basic), ~$0.005-0.01 (standard/deep)' },
      inputSchema: z.object({
        phone_number: z.string().describe('Phone number to look up'),
        depth: z.enum(['basic', 'standard', 'deep']).optional().describe('Lookup depth: basic (free validation), standard (+ line type, carrier), deep (+ caller name, everything)'),
      }),
      _meta: {
        labels: {
          category: "intelligence",
          access: "read",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        const phone = toE164(params.phone_number);
        const depth = params.depth || 'basic';

        try {
          const fields: string[] = [];
          if (depth === 'standard' || depth === 'deep') {
            fields.push('line_type_intelligence');
          }
          if (depth === 'deep') {
            fields.push('caller_name');
          }

          const result: any = await this.client.execute(c =>
            (c as any).lookups.v2.phoneNumbers(phone).fetch({
              fields: fields.length > 0 ? fields.join(',') : undefined,
            })
          );

          const info: string[] = [
            `Phone Number: ${result.phoneNumber}`,
            `Valid: ${result.valid ? 'Yes' : 'No'}`,
            `Country: ${result.countryCode}`,
            `National Format: ${result.nationalFormat}`,
          ];

          if (result.lineTypeIntelligence) {
            info.push(`Line Type: ${result.lineTypeIntelligence.type || 'unknown'}`);
            info.push(`Carrier: ${result.lineTypeIntelligence.carrier_name || 'unknown'}`);
          }

          if (result.callerName) {
            info.push(`Caller Name: ${result.callerName.caller_name || 'unknown'}`);
            info.push(`Caller Type: ${result.callerName.caller_type || 'unknown'}`);
          }

          return this.textResult(info.join('\n'));
        } catch (err: any) {
          return this.errorResult(`Lookup failed: ${err.message}`);
        }
      },
    });
  }
}
