import { z } from 'zod';
import { BasePack } from '../base-pack.js';
import type { TwilioClient } from '../../client.js';
import * as renderer from '../../apps/renderer.js';
import { toE164, timeAgo } from '../../utils/validators.js';

export class ConversationsPack extends BasePack {
  readonly name = 'conversations';
  readonly description = 'Twilio Conversations API — omnichannel messaging, threads, participants';
  readonly keywords = ['conversation', 'chat', 'thread', 'messaging', 'omnichannel', 'inbox'];

  constructor(client: TwilioClient) {
    super(client);
  }

  setup(): void {
    // ─── twilio_inbox ───
    this.registerTool({
      name: 'twilio_inbox',
      description: 'View all conversations in your account — active, inactive, or closed.',
      category: 'conversations',
      safety: 'green',
      tier: 2,
      isApp: true,
      annotations: { readOnlyHint: true },
      inputSchema: z.object({
        limit: z.number().min(1).max(100).optional().describe('Max conversations to fetch (default 20)'),
        state: z.enum(['active', 'inactive', 'closed']).optional().describe('Filter by conversation state'),
      }),
      _meta: {
        labels: {
          category: "conversations",
          access: "read",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        try {
          const limit = params.limit || 20;
          const conversations = await this.client.execute(c =>
            c.conversations.v1.conversations.list({ limit })
          );

          // Filter by state if specified
          const filtered = params.state
            ? conversations.filter(c => c.state === params.state)
            : conversations;

          // Fetch participant counts for each conversation
          const conversationData = await Promise.all(
            filtered.map(async conv => {
              try {
                const participants = await this.client.execute(c =>
                  c.conversations.v1.conversations(conv.sid).participants.list()
                );
                return { conv, participantCount: participants.length };
              } catch {
                return { conv, participantCount: 0 };
              }
            })
          );

          // Build inbox table
          const rows = conversationData.map(({ conv, participantCount }) => {
            let stateBadge: string;
            if (conv.state === 'active') stateBadge = renderer.badge('Active', 'success');
            else if (conv.state === 'inactive') stateBadge = renderer.badge('Inactive', 'warning');
            else stateBadge = renderer.badge('Closed', 'muted');

            return [
              `<strong>${conv.friendlyName || conv.sid.slice(0, 12) + '...'}</strong>`,
              stateBadge,
              String(participantCount),
              timeAgo(conv.dateCreated),
              conv.dateUpdated ? timeAgo(conv.dateUpdated) : '—',
            ];
          });

          const html = renderer.appLayout(
            '📬 Conversations Inbox',
            `${filtered.length} conversation${filtered.length === 1 ? '' : 's'}${params.state ? ` (${params.state})` : ''}`,
            `
            <div class="card">
              <div class="card-header">📊 Overview</div>
              <div class="card-grid card-grid-4" style="margin-top: 8px;">
                ${renderer.metricCard(String(conversationData.length), 'Total')}
                ${renderer.metricCard(String(conversationData.filter(c => c.conv.state === 'active').length), 'Active')}
                ${renderer.metricCard(String(conversationData.filter(c => c.conv.state === 'inactive').length), 'Inactive')}
                ${renderer.metricCard(String(conversationData.filter(c => c.conv.state === 'closed').length), 'Closed')}
              </div>
            </div>

            <div class="card">
              <div class="card-header">💬 Conversations</div>
              ${renderer.table(
                ['Name/SID', 'State', 'Participants', 'Created', 'Updated'],
                rows
              )}
            </div>
            `
          );

          return this.appResult(html,
            `Conversations Inbox (${filtered.length}):\n` +
            conversationData.map(({ conv, participantCount }) =>
              `  ${conv.friendlyName || conv.sid} — ${conv.state} — ${participantCount} participants — ${timeAgo(conv.dateCreated)}`
            ).join('\n')
          );
        } catch (err: any) {
          return this.errorResult(`Failed to fetch inbox: ${err.message}`);
        }
      },
    });

    // ─── twilio_conversation_create ───
    this.registerTool({
      name: 'twilio_conversation_create',
      description: 'Create a new conversation thread and add participants (SMS, WhatsApp, or chat identity).',
      category: 'conversations',
      safety: 'yellow',
      tier: 2,
      annotations: { costHint: false },
      inputSchema: z.object({
        name: z.string().describe('Conversation friendly name'),
        participants: z.array(z.object({
          identity: z.string().optional().describe('Chat identity (username)'),
          messaging_binding: z.object({
            address: z.string().describe('Phone number'),
            proxy_address: z.string().describe('Twilio phone number to use'),
            type: z.enum(['sms', 'whatsapp']).describe('Messaging channel'),
          }).optional().describe('SMS/WhatsApp binding'),
        })).describe('Participants to add'),
      }),
      _meta: {
        labels: {
          category: "conversations",
          access: "write",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        try {
          // Create conversation
          const conversation = await this.client.execute(c =>
            c.conversations.v1.conversations.create({
              friendlyName: params.name,
            })
          );

          // Add participants
          const participants = await Promise.all(
            params.participants.map((p: any) => {
              const participantData: any = {};
              
              if (p.identity) {
                participantData.identity = p.identity;
              }
              
              if (p.messaging_binding) {
                participantData['messagingBinding.address'] = toE164(p.messaging_binding.address);
                participantData['messagingBinding.proxyAddress'] = toE164(p.messaging_binding.proxy_address);
                
                if (p.messaging_binding.type === 'whatsapp') {
                  participantData['messagingBinding.address'] = `whatsapp:${participantData['messagingBinding.address']}`;
                  participantData['messagingBinding.proxyAddress'] = `whatsapp:${participantData['messagingBinding.proxyAddress']}`;
                }
              }

              return this.client.execute(c =>
                c.conversations.v1.conversations(conversation.sid).participants.create(participantData)
              );
            })
          );

          return this.textResult(
            `Conversation created!\n` +
            `  Name: ${conversation.friendlyName}\n` +
            `  SID: ${conversation.sid}\n` +
            `  Participants: ${participants.length}\n` +
            participants.map(p => {
              if (p.identity) return `    - ${p.identity} (chat)`;
              if (p.messagingBinding) {
                const binding = p.messagingBinding as any;
                return `    - ${binding.address} (${binding.type})`;
              }
              return `    - ${p.sid}`;
            }).join('\n')
          );
        } catch (err: any) {
          return this.errorResult(`Failed to create conversation: ${err.message}`);
        }
      },
    });

    // ─── twilio_omnichannel_send ───
    this.registerTool({
      name: 'twilio_omnichannel_send',
      description: 'Send a message via SMS or WhatsApp with smart fallback. Tries preferred channel first, falls back to other if unavailable.',
      category: 'conversations',
      safety: 'yellow',
      tier: 2,
      annotations: { costHint: true, estimatedCost: '~$0.0079/SMS, ~$0.005/WhatsApp (US)' },
      inputSchema: z.object({
        to: z.string().describe('Recipient phone number'),
        body: z.string().describe('Message text'),
        channel_preference: z.enum(['sms_first', 'whatsapp_first']).optional().describe('Preferred channel (default sms_first)'),
        from: z.string().optional().describe('Sender phone number (auto-selected if omitted)'),
      }),
      _meta: {
        labels: {
          category: "conversations",
          access: "write",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        try {
          const to = toE164(params.to);
          const preference = params.channel_preference || 'sms_first';

          // Get sender number
          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;
          }
          from = toE164(from);

          const channels = preference === 'sms_first' 
            ? [{ type: 'sms', toFormat: to, fromFormat: from }, { type: 'whatsapp', toFormat: `whatsapp:${to}`, fromFormat: `whatsapp:${from}` }]
            : [{ type: 'whatsapp', toFormat: `whatsapp:${to}`, fromFormat: `whatsapp:${from}` }, { type: 'sms', toFormat: to, fromFormat: from }];

          let lastError: any = null;
          
          for (const channel of channels) {
            try {
              const message = await this.client.execute(c =>
                c.messages.create({
                  to: channel.toFormat,
                  from: channel.fromFormat,
                  body: params.body,
                })
              );

              return this.textResult(
                `Message sent via ${channel.type.toUpperCase()}!\n` +
                `  SID: ${message.sid}\n` +
                `  To: ${to}\n` +
                `  From: ${from}\n` +
                `  Channel: ${channel.type}\n` +
                `  Status: ${message.status}\n` +
                `  Cost: ${message.price ? `$${Math.abs(parseFloat(message.price))}` : 'pending'}`
              );
            } catch (err: any) {
              lastError = err;
              // Try next channel
              continue;
            }
          }

          return this.errorResult(`Failed to send via both channels. Last error: ${lastError?.message || 'Unknown'}`);
        } catch (err: any) {
          return this.errorResult(`Failed to send message: ${err.message}`);
        }
      },
    });
  }
}
