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

export class StudioPack extends BasePack {
  readonly name = 'studio';
  readonly description = 'Create, manage, and execute Twilio Studio flows';
  readonly keywords = ['studio', 'flow', 'workflow', 'ivr', 'automation', 'widget', 'webhook'];

  setup(): void {
    // ─── twilio_flow_create ───
    this.registerTool({
      name: 'twilio_flow_create',
      description: 'Create a new Twilio Studio flow from widget descriptions. Build voice/messaging flows programmatically.',
      category: 'studio',
      safety: 'yellow',
      tier: 2,
      isApp: true,
      inputSchema: z.object({
        name: z.string().describe('Flow name'),
        description: z.string().describe('Flow description'),
        type: z.enum(['voice', 'messaging', 'both']).describe('Flow type — what triggers it'),
        widgets: z.array(z.object({
          type: z.enum(['say', 'gather', 'send_message', 'connect', 'record', 'http', 'split']).describe('Widget type'),
          config: z.record(z.any()).describe('Widget configuration (type-specific)'),
        })).describe('Array of widgets to include in the flow'),
        assign_to_number: z.string().optional().describe('Phone number to assign this flow to'),
      }),
      _meta: {
        labels: {
          category: "studio",
          access: "write",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        try {
          // Build the Studio flow definition
          const flowWidgets: studioBuilder.StudioWidget[] = [
            studioBuilder.triggerWidget({
              incomingMessage: params.type === 'messaging' || params.type === 'both' ? params.widgets[0]?.config.name || 'Widget1' : undefined,
              incomingCall: params.type === 'voice' || params.type === 'both' ? params.widgets[0]?.config.name || 'Widget1' : undefined,
            }),
          ];

          // Build each widget
          params.widgets.forEach((widget: any, idx: number) => {
            const name = widget.config.name || `Widget${idx + 1}`;
            const nextName = idx < params.widgets.length - 1 ? (params.widgets[idx + 1].config.name || `Widget${idx + 2}`) : undefined;

            switch (widget.type) {
              case 'say':
                flowWidgets.push(studioBuilder.sayWidget(
                  name,
                  widget.config.message || 'Hello',
                  { voice: widget.config.voice, next: nextName }
                ));
                break;

              case 'gather':
                flowWidgets.push(studioBuilder.gatherWidget(
                  name,
                  widget.config.prompt || 'Press a key',
                  {
                    numDigits: widget.config.numDigits,
                    timeout: widget.config.timeout,
                    voice: widget.config.voice,
                    onComplete: nextName,
                  }
                ));
                break;

              case 'send_message':
                flowWidgets.push(studioBuilder.sendMessageWidget(
                  name,
                  widget.config.body || 'Message',
                  {
                    to: widget.config.to,
                    from: widget.config.from,
                    mediaUrl: widget.config.mediaUrl,
                    next: nextName,
                  }
                ));
                break;

              case 'connect':
                flowWidgets.push(studioBuilder.connectWidget(
                  name,
                  widget.config.toNumber,
                  {
                    callerId: widget.config.callerId,
                    timeout: widget.config.timeout,
                    next: nextName,
                  }
                ));
                break;

              case 'record':
                flowWidgets.push(studioBuilder.recordWidget(
                  name,
                  {
                    playBeep: widget.config.playBeep,
                    maxLength: widget.config.maxLength,
                    transcribe: widget.config.transcribe,
                    onComplete: nextName,
                  }
                ));
                break;

              case 'http':
                flowWidgets.push(studioBuilder.httpWidget(
                  name,
                  widget.config.url,
                  {
                    method: widget.config.method,
                    body: widget.config.body,
                    headers: widget.config.headers,
                    onSuccess: nextName,
                  }
                ));
                break;

              case 'split':
                flowWidgets.push(studioBuilder.splitWidget(
                  name,
                  widget.config.variable,
                  widget.config.conditions || [],
                  { noMatch: nextName }
                ));
                break;
            }
          });

          const flowDefinition = studioBuilder.buildFlow(params.description, flowWidgets);

          // Create the flow via API
          const flow: any = await this.client.execute(c =>
            (c as any).studio.v2.flows.create({
              friendlyName: params.name,
              status: 'draft',
              definition: flowDefinition,
            })
          );

          // Optionally assign to a phone number
          if (params.assign_to_number) {
            const phone = toE164(params.assign_to_number);
            const numbers = await this.client.execute(c =>
              c.incomingPhoneNumbers.list({ phoneNumber: phone, limit: 1 })
            );

            if (numbers.length > 0) {
              const updateParams: any = {};
              if (params.type === 'messaging' || params.type === 'both') {
                updateParams.smsUrl = `https://webhooks.twilio.com/v1/Accounts/${this.client.accountSid}/Flows/${flow.sid}`;
              }
              if (params.type === 'voice' || params.type === 'both') {
                updateParams.voiceUrl = `https://webhooks.twilio.com/v1/Accounts/${this.client.accountSid}/Flows/${flow.sid}`;
              }

              await this.client.execute(c =>
                c.incomingPhoneNumbers(numbers[0].sid).update(updateParams)
              );
            }
          }

          // Build visual representation
          const widgetList = flowWidgets.slice(1).map(w => 
            `<div class="flow-node">${w.name} <span class="muted">(${w.type})</span></div>`
          ).join('<span class="flow-arrow">→</span>');

          const html = renderer.appLayout(
            '🔄 Studio Flow Created',
            `${params.name} — ${params.widgets.length} widget${params.widgets.length === 1 ? '' : 's'}`,
            `
            <div class="card">
              <div class="card-header">Flow Details</div>
              <div style="padding: 8px 0;">
                <strong>SID:</strong> ${flow.sid}<br>
                <strong>Type:</strong> ${renderer.badge(params.type, 'info')}<br>
                <strong>Status:</strong> ${renderer.badge('Draft', 'warning')}<br>
                ${params.assign_to_number ? `<strong>Assigned to:</strong> ${params.assign_to_number}<br>` : ''}
              </div>
            </div>

            <div class="card">
              <div class="card-header">Flow Diagram</div>
              <div style="padding: 12px; overflow-x: auto;">
                <div class="flow-node active">Trigger</div>
                <span class="flow-arrow">→</span>
                ${widgetList}
              </div>
            </div>

            <div class="muted small" style="margin-top: 8px;">
              Flow is in <strong>draft</strong> status. Publish it in Twilio Console to activate.
            </div>
            `
          );

          return this.appResult(
            html,
            `✅ Studio flow created!\n\n` +
            `  Name: ${params.name}\n` +
            `  SID: ${flow.sid}\n` +
            `  Type: ${params.type}\n` +
            `  Widgets: ${params.widgets.length}\n` +
            `  Status: Draft\n` +
            (params.assign_to_number ? `  Assigned to: ${params.assign_to_number}\n` : '') +
            `\nFlow is ready to use. Publish it in Twilio Console to activate.`
          );
        } catch (err: any) {
          return this.errorResult(`Flow creation failed: ${err.message}`);
        }
      },
    });

    // ─── twilio_flow_list ───
    this.registerTool({
      name: 'twilio_flow_list',
      description: 'List all Studio flows in your account with status and revision information.',
      category: 'studio',
      safety: 'green',
      tier: 2,
      isApp: true,
      annotations: { readOnlyHint: true },
      inputSchema: z.object({
        status: z.enum(['draft', 'published']).optional().describe('Filter by flow status'),
      }),
      _meta: {
        labels: {
          category: "studio",
          access: "read",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        try {
          let flows: any[] = await this.client.execute(c =>
            (c as any).studio.v2.flows.list({ limit: 100 })
          );

          // Apply status filter if provided
          if (params.status) {
            flows = flows.filter((f: any) => f.status === params.status);
          }

          if (flows.length === 0) {
            return this.textResult('No Studio flows found.');
          }

          // Build HTML table
          const rows = flows.map((f: any) => [
            `<strong>${f.friendlyName}</strong>`,
            f.status === 'published' 
              ? renderer.badge('Published', 'success')
              : renderer.badge('Draft', 'warning'),
            `<span class="small muted">${new Date(f.dateCreated).toLocaleDateString()}</span>`,
            `<span class="small muted">${new Date(f.dateUpdated).toLocaleDateString()}</span>`,
            `<span class="small">${f.revision || 1}</span>`,
          ]);

          const html = renderer.appLayout(
            '🔄 Studio Flows',
            `${flows.length} flow${flows.length === 1 ? '' : 's'} in your account`,
            `
            <div class="card">
              ${renderer.table(
                ['Name', 'Status', 'Date Created', 'Date Updated', 'Revision'],
                rows
              )}
            </div>
            `
          );

          return this.appResult(
            html,
            `Studio Flows (${flows.length}):\n` +
            flows.map((f: any) => `  ${f.friendlyName} — ${f.status} (rev ${f.revision || 1})`).join('\n')
          );
        } catch (err: any) {
          return this.errorResult(`Failed to list flows: ${err.message}`);
        }
      },
    });

    // ─── twilio_flow_execute ───
    this.registerTool({
      name: 'twilio_flow_execute',
      description: 'Execute a Studio flow programmatically. Trigger a flow with custom parameters and a target phone number.',
      category: 'studio',
      safety: 'yellow',
      tier: 2,
      annotations: { costHint: true, estimatedCost: 'Varies by flow actions (messaging/voice costs apply)' },
      inputSchema: z.object({
        flow_sid: z.string().describe('Flow SID or flow name to execute'),
        to: z.string().describe('Target phone number to execute the flow for'),
        from: z.string().optional().describe('Phone number to use as sender (auto-selected if omitted)'),
        parameters: z.record(z.string()).optional().describe('Key-value parameters to pass to the flow'),
      }),
      _meta: {
        labels: {
          category: "studio",
          access: "read",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        const to = toE164(params.to);
        if (!isValidPhone(to)) {
          return this.errorResult(`Invalid phone number: ${params.to}`);
        }

        try {
          // Find flow by SID or name
          let flowSid = params.flow_sid;
          if (!isSid(flowSid, 'FW')) {
            const flows: any[] = await this.client.execute(c =>
              (c as any).studio.v2.flows.list({ limit: 100 })
            );
            const flow: any = flows.find((f: any) =>
              f.friendlyName.toLowerCase() === params.flow_sid.toLowerCase()
            );
            if (!flow) {
              return this.errorResult(`Flow not found: ${params.flow_sid}`);
            }
            flowSid = flow.sid;
          }

          // Auto-select from number if not provided
          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;
          }

          // Execute the flow
          const execution: any = await this.client.execute(c =>
            (c as any).studio.v2.flows(flowSid).executions.create({
              to,
              from: toE164(from!),
              parameters: params.parameters || {},
            })
          );

          return this.textResult(
            `✅ Flow execution started!\n\n` +
            `  Execution SID: ${execution.sid}\n` +
            `  Flow: ${flowSid}\n` +
            `  To: ${to}\n` +
            `  From: ${from}\n` +
            `  Status: ${execution.status}\n` +
            `  Context: ${execution.context?.contact ? execution.context.contact.channel.address : 'N/A'}`
          );
        } catch (err: any) {
          return this.errorResult(`Flow execution failed: ${err.message}`);
        }
      },
    });

    // ─── twilio_flow_diagram ───
    this.registerTool({
      name: 'twilio_flow_diagram',
      description: 'Fetch and visualize a Studio flow definition. Shows widget names, types, and connections.',
      category: 'studio',
      safety: 'green',
      tier: 2,
      isApp: true,
      annotations: { readOnlyHint: true },
      inputSchema: z.object({
        flow_sid: z.string().describe('Flow SID to visualize'),
      }),
      _meta: {
        labels: {
          category: "studio",
          access: "read",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        try {
          // Fetch flow definition
          const flow: any = await this.client.execute(c =>
            (c as any).studio.v2.flows(params.flow_sid).fetch()
          );

          const definition = flow.definition;
          if (!definition || !definition.states) {
            return this.errorResult('Flow definition not available or empty.');
          }

          const widgets = definition.states;

          // Build visual diagram
          const widgetNodes = widgets.map((w: any) => {
            const isActive = w.name === 'Trigger';
            return `<div class="flow-node ${isActive ? 'active' : ''}">
              <strong>${w.name}</strong><br>
              <span class="muted small">${w.type}</span>
            </div>`;
          }).join('<span class="flow-arrow">→</span>');

          // Build widget details table
          const rows = widgets.map((w: any) => {
            const transitions = w.transitions
              ?.filter((t: any) => t.next)
              .map((t: any) => `${t.event} → ${t.next}`)
              .join(', ') || '—';

            return [
              `<strong>${w.name}</strong>`,
              renderer.badge(w.type, 'info'),
              `<span class="small">${transitions}</span>`,
            ];
          });

          const html = renderer.appLayout(
            '📊 Studio Flow Diagram',
            `${flow.friendlyName} — ${widgets.length} widget${widgets.length === 1 ? '' : 's'}`,
            `
            <div class="card">
              <div class="card-header">Flow Information</div>
              <div style="padding: 8px 0;">
                <strong>SID:</strong> ${flow.sid}<br>
                <strong>Status:</strong> ${flow.status === 'published' ? renderer.badge('Published', 'success') : renderer.badge('Draft', 'warning')}<br>
                <strong>Revision:</strong> ${flow.revision || 1}<br>
                <strong>Description:</strong> ${definition.description || '(none)'}
              </div>
            </div>

            <div class="card">
              <div class="card-header">Flow Diagram</div>
              <div style="padding: 12px; overflow-x: auto;">
                ${widgetNodes}
              </div>
            </div>

            <div class="card">
              <div class="card-header">Widget Details</div>
              ${renderer.table(
                ['Widget Name', 'Type', 'Transitions'],
                rows
              )}
            </div>
            `
          );

          return this.appResult(
            html,
            `Studio Flow: ${flow.friendlyName}\n` +
            `SID: ${flow.sid}\n` +
            `Widgets: ${widgets.length}\n` +
            `Status: ${flow.status}\n\n` +
            widgets.map((w: any) => `  ${w.name} (${w.type})`).join('\n')
          );
        } catch (err: any) {
          return this.errorResult(`Failed to fetch flow diagram: ${err.message}`);
        }
      },
    });

    // ─── twilio_webhook_wizard ───
    this.registerTool({
      name: 'twilio_webhook_wizard',
      description: 'Generate and deploy a Twilio Function webhook from a natural language description. Automatically creates TwiML responses for SMS/voice.',
      category: 'studio',
      safety: 'yellow',
      tier: 2,
      inputSchema: z.object({
        name: z.string().describe('Function name'),
        description: z.string().describe('What the webhook should do (e.g., "reply with a welcome message", "forward call to +1234567890")'),
        trigger: z.enum(['sms_received', 'call_received']).describe('When this webhook should trigger'),
        assign_to_number: z.string().optional().describe('Phone number to assign this webhook to'),
      }),
      _meta: {
        labels: {
          category: "studio",
          access: "read",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        try {
          // Generate TwiML code based on description
          let code: string;
          const desc = params.description.toLowerCase();

          if (params.trigger === 'sms_received') {
            // Generate SMS handler
            if (desc.includes('reply') || desc.includes('respond')) {
              const message = desc.match(/["'](.+?)["']/) || ['', 'Thanks for your message!'];
              code = `exports.handler = function(context, event, callback) {
  const twiml = new Twilio.twiml.MessagingResponse();
  twiml.message('${message[1].replace(/'/g, "\\'")}');
  return callback(null, twiml);
};`;
            } else if (desc.includes('forward')) {
              code = `exports.handler = function(context, event, callback) {
  const twiml = new Twilio.twiml.MessagingResponse();
  // Forward logic: implement custom forwarding here
  twiml.message('Message received and will be forwarded.');
  return callback(null, twiml);
};`;
            } else {
              code = `exports.handler = function(context, event, callback) {
  const twiml = new Twilio.twiml.MessagingResponse();
  // Auto-generated webhook: ${params.description}
  twiml.message('Message received. Processing...');
  return callback(null, twiml);
};`;
            }
          } else {
            // Generate voice handler
            if (desc.includes('say') || desc.includes('speak')) {
              const message = desc.match(/["'](.+?)["']/) || ['', 'Thank you for calling.'];
              code = `exports.handler = function(context, event, callback) {
  const twiml = new Twilio.twiml.VoiceResponse();
  twiml.say('${message[1].replace(/'/g, "\\'")}');
  return callback(null, twiml);
};`;
            } else if (desc.includes('forward') || desc.includes('connect')) {
              const number = desc.match(/\+?1?\d{10,}/);
              code = `exports.handler = function(context, event, callback) {
  const twiml = new Twilio.twiml.VoiceResponse();
  twiml.say('Connecting you now.');
  twiml.dial('${number ? number[0] : '+1234567890'}');
  return callback(null, twiml);
};`;
            } else if (desc.includes('record') || desc.includes('voicemail')) {
              code = `exports.handler = function(context, event, callback) {
  const twiml = new Twilio.twiml.VoiceResponse();
  twiml.say('Please leave a message after the beep.');
  twiml.record({ maxLength: 120, transcribe: true });
  return callback(null, twiml);
};`;
            } else {
              code = `exports.handler = function(context, event, callback) {
  const twiml = new Twilio.twiml.VoiceResponse();
  // Auto-generated webhook: ${params.description}
  twiml.say('Hello, thank you for calling.');
  return callback(null, twiml);
};`;
            }
          }

          // NOTE: Actual Serverless API deployment would happen here
          // For now, we'll return the generated code and a mock URL
          const functionUrl = `https://your-service-xxxx.twil.io/${params.name.toLowerCase().replace(/\s+/g, '-')}`;

          // Optionally assign to a phone number
          if (params.assign_to_number) {
            const phone = toE164(params.assign_to_number);
            const numbers = await this.client.execute(c =>
              c.incomingPhoneNumbers.list({ phoneNumber: phone, limit: 1 })
            );

            if (numbers.length > 0) {
              const updateParams: any = {};
              if (params.trigger === 'sms_received') {
                updateParams.smsUrl = functionUrl;
              } else {
                updateParams.voiceUrl = functionUrl;
              }

              // Commented out actual deployment for safety
              // await this.client.execute(c =>
              //   c.incomingPhoneNumbers(numbers[0].sid).update(updateParams)
              // );
            }
          }

          return this.textResult(
            `✅ Webhook code generated!\n\n` +
            `Name: ${params.name}\n` +
            `Trigger: ${params.trigger}\n` +
            `Function URL: ${functionUrl}\n` +
            (params.assign_to_number ? `Assigned to: ${params.assign_to_number}\n` : '') +
            `\n--- Generated Code ---\n${code}\n\n` +
            `⚠️  Note: Actual Serverless deployment requires Twilio Functions API setup.\n` +
            `Copy this code into Twilio Console > Functions to deploy.`
          );
        } catch (err: any) {
          return this.errorResult(`Webhook wizard failed: ${err.message}`);
        }
      },
    });
  }
}
