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

export class ServerlessPack extends BasePack {
  readonly name = 'serverless';
  readonly description = 'Deploy and manage Twilio Functions and serverless infrastructure';
  readonly keywords = ['function', 'serverless', 'deploy', 'runtime', 'code', 'lambda'];

  setup(): void {
    // ─── twilio_deploy_function ───
    this.registerTool({
      name: 'twilio_deploy_function',
      description: 'Deploy a Twilio Function to the serverless runtime. Creates service, function, build, and deployment.',
      category: 'serverless',
      safety: 'yellow',
      tier: 2,
      annotations: { idempotentHint: false, costHint: false },
      inputSchema: z.object({
        service_name: z.string().describe('Name of the serverless service'),
        function_name: z.string().describe('Name of the function (e.g., "hello-world")'),
        code: z.string().describe('Function code (JavaScript)'),
        description: z.string().optional().describe('Function description'),
        environment: z.enum(['production', 'staging']).optional().describe('Target environment (default: production)'),
        env_vars: z.record(z.string()).optional().describe('Environment variables as key-value pairs'),
      }),
      _meta: {
        labels: {
          category: "serverless",
          access: "write",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        try {
          const environment = params.environment || 'production';

          // Step 1: Find or create service
          const services: any[] = await this.client.execute(c =>
            (c as any).serverless.v1.services.list({ limit: 100 })
          );

          let service = services.find((s: any) => s.friendlyName === params.service_name);
          if (!service) {
            service = await this.client.execute(c =>
              (c as any).serverless.v1.services.create({
                uniqueName: params.service_name.toLowerCase().replace(/\s+/g, '-'),
                friendlyName: params.service_name,
              })
            );
          }

          const serviceSid = service.sid;

          // Step 2: Create function
          const functionPath = `/${params.function_name}`;
          const twilioFunction: any = await this.client.execute(c =>
            (c as any).serverless.v1.services(serviceSid).functions.create({
              friendlyName: params.function_name,
            })
          );

          const functionSid = twilioFunction.sid;

          // Step 3: Create function version with code
          const functionVersion: any = await this.client.execute(c =>
            (c as any).serverless.v1.services(serviceSid)
              .functions(functionSid)
              .functionVersions.create({
                path: functionPath,
                visibility: 'public',
                content: params.code,
              })
          );

          // Step 4: Find or create environment
          const environments: any[] = await this.client.execute(c =>
            (c as any).serverless.v1.services(serviceSid).environments.list()
          );

          let env = environments.find((e: any) => e.uniqueName === environment);
          if (!env) {
            env = await this.client.execute(c =>
              (c as any).serverless.v1.services(serviceSid).environments.create({
                uniqueName: environment,
                domainSuffix: environment,
              })
            );
          }

          const envSid = env.sid;

          // Step 5: Set environment variables if provided
          if (params.env_vars) {
            for (const [key, value] of Object.entries(params.env_vars)) {
              await this.client.execute(c =>
                (c as any).serverless.v1.services(serviceSid)
                  .environments(envSid)
                  .variables.create({
                    key,
                    value,
                  })
              );
            }
          }

          // Step 6: Create build
          const build: any = await this.client.execute(c =>
            (c as any).serverless.v1.services(serviceSid).builds.create({
              functionVersions: [functionVersion.sid],
              dependencies: JSON.stringify({}),
            })
          );

          // Step 7: Create deployment
          const deployment: any = await this.client.execute(c =>
            (c as any).serverless.v1.services(serviceSid)
              .environments(envSid)
              .deployments.create({
                buildSid: build.sid,
              })
          );

          // Construct function URL
          const domain = env.domainName;
          const functionUrl = `https://${domain}${functionPath}`;

          return this.textResult(
            `Function deployed successfully! 🚀\n` +
            `  Service: ${params.service_name} (${serviceSid})\n` +
            `  Function: ${params.function_name} (${functionSid})\n` +
            `  Environment: ${environment} (${envSid})\n` +
            `  Build: ${build.sid}\n` +
            `  Deployment: ${deployment.sid}\n` +
            `  URL: ${functionUrl}\n\n` +
            `Test with: curl ${functionUrl}`
          );
        } catch (err: any) {
          return this.errorResult(`Deployment failed: ${err.message}`);
        }
      },
    });

    // ─── twilio_deploy_status ───
    this.registerTool({
      name: 'twilio_deploy_status',
      description: 'View deployment status for serverless services. Shows services, environments, functions, and builds.',
      category: 'serverless',
      safety: 'green',
      tier: 2,
      isApp: true,
      annotations: { readOnlyHint: true },
      inputSchema: z.object({
        service_name: z.string().optional().describe('Filter by service name (omit to show all)'),
      }),
      _meta: {
        labels: {
          category: "serverless",
          access: "write",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        try {
          // Fetch all services
          const services: any[] = await this.client.execute(c =>
            (c as any).serverless.v1.services.list({ limit: 100 })
          );

          const filteredServices = params.service_name
            ? services.filter((s: any) => s.friendlyName === params.service_name || s.uniqueName === params.service_name)
            : services;

          if (filteredServices.length === 0) {
            return this.textResult(
              params.service_name
                ? `No service found with name: ${params.service_name}`
                : 'No serverless services found. Deploy your first function with twilio_deploy_function.'
            );
          }

          // Build service cards
          const serviceCards: string[] = [];

          for (const service of filteredServices) {
            const serviceSid = service.sid;

            // Fetch environments
            const environments: any[] = await this.client.execute(c =>
              (c as any).serverless.v1.services(serviceSid).environments.list()
            );

            // Fetch functions
            const functions: any[] = await this.client.execute(c =>
              (c as any).serverless.v1.services(serviceSid).functions.list()
            );

            // Fetch latest build
            const builds: any[] = await this.client.execute(c =>
              (c as any).serverless.v1.services(serviceSid).builds.list({ limit: 1 })
            );
            const latestBuild = builds[0];

            // Environment rows
            const envRows: string[] = environments.map((env: any) => {
              const envUrl = `https://${env.domainName}`;
              const functionsList = functions.map((f: any) => f.friendlyName).join(', ') || 'None';
              return `
                <div class="list-item">
                  ${renderer.statusDot('blue')}
                  <div style="flex: 1;">
                    <strong>${env.uniqueName}</strong>
                    <div class="small muted">${envUrl}</div>
                    <div class="small dim">Functions: ${functionsList}</div>
                  </div>
                  ${renderer.badge('Active', 'success')}
                </div>
              `;
            });

            const buildStatus = latestBuild
              ? `${renderer.badge(latestBuild.status, latestBuild.status === 'completed' ? 'success' : 'warning')} ${timeAgo(latestBuild.dateCreated)}`
              : renderer.badge('No builds', 'muted');

            serviceCards.push(`
              <div class="card">
                <div class="card-header">📦 ${service.friendlyName}</div>
                <div class="small muted" style="margin-bottom: 12px;">SID: ${serviceSid}</div>
                
                <div style="margin-bottom: 12px;">
                  <div class="small" style="margin-bottom: 4px;"><strong>Latest Build:</strong> ${buildStatus}</div>
                  <div class="small"><strong>Functions:</strong> ${functions.length}</div>
                </div>

                <div class="card-header small">Environments</div>
                ${envRows.length > 0 ? envRows.join('') : '<p class="muted small">No environments</p>'}
              </div>
            `);
          }

          const html = renderer.appLayout(
            '🚀 Deployment Status',
            params.service_name ? `Service: ${params.service_name}` : `${filteredServices.length} service(s)`,
            serviceCards.join('')
          );

          return this.appResult(html,
            `Found ${filteredServices.length} service(s). Total functions: ${filteredServices.reduce((sum: number, s: any) => sum + (s.functions?.length || 0), 0)}`
          );
        } catch (err: any) {
          return this.errorResult(`Failed to fetch deployment status: ${err.message}`);
        }
      },
    });

    // ─── twilio_env_vars ───
    this.registerTool({
      name: 'twilio_env_vars',
      description: 'Manage environment variables for serverless functions. Supports list, set, and delete operations.',
      category: 'serverless',
      safety: 'yellow',
      tier: 2,
      annotations: { idempotentHint: false },
      inputSchema: z.object({
        service_name: z.string().describe('Service name'),
        environment: z.enum(['production', 'staging']).describe('Environment name'),
        action: z.enum(['list', 'set', 'delete']).describe('Action to perform'),
        key: z.string().optional().describe('Variable key (required for set/delete)'),
        value: z.string().optional().describe('Variable value (required for set)'),
      }),
      _meta: {
        labels: {
          category: "serverless",
          access: "read",
          complexity: "simple"
        }
      },
      handler: async (params) => {
        try {
          // Find service
          const services: any[] = await this.client.execute(c =>
            (c as any).serverless.v1.services.list({ limit: 100 })
          );

          const service = services.find((s: any) => 
            s.friendlyName === params.service_name || s.uniqueName === params.service_name
          );

          if (!service) {
            return this.errorResult(`Service not found: ${params.service_name}`);
          }

          const serviceSid = service.sid;

          // Find environment
          const environments: any[] = await this.client.execute(c =>
            (c as any).serverless.v1.services(serviceSid).environments.list()
          );

          const env = environments.find((e: any) => e.uniqueName === params.environment);
          if (!env) {
            return this.errorResult(`Environment not found: ${params.environment}`);
          }

          const envSid = env.sid;

          // Perform action
          switch (params.action) {
            case 'list': {
              const variables: any[] = await this.client.execute(c =>
                (c as any).serverless.v1.services(serviceSid)
                  .environments(envSid)
                  .variables.list()
              );

              if (variables.length === 0) {
                return this.textResult('No environment variables set.');
              }

              const varList = variables.map((v: any) => `  ${v.key} = ${v.value}`).join('\n');
              return this.textResult(
                `Environment variables for ${params.service_name} (${params.environment}):\n${varList}`
              );
            }

            case 'set': {
              if (!params.key || !params.value) {
                return this.errorResult('key and value are required for action=set');
              }

              // Check if variable exists
              const variables: any[] = await this.client.execute(c =>
                (c as any).serverless.v1.services(serviceSid)
                  .environments(envSid)
                  .variables.list()
              );

              const existing = variables.find((v: any) => v.key === params.key);
              if (existing) {
                // Update
                await this.client.execute(c =>
                  (c as any).serverless.v1.services(serviceSid)
                    .environments(envSid)
                    .variables(existing.sid)
                    .update({ value: params.value })
                );
              } else {
                // Create
                await this.client.execute(c =>
                  (c as any).serverless.v1.services(serviceSid)
                    .environments(envSid)
                    .variables.create({
                      key: params.key,
                      value: params.value,
                    })
                );
              }

              return this.textResult(
                `Environment variable ${existing ? 'updated' : 'created'}:\n` +
                `  ${params.key} = ${params.value}`
              );
            }

            case 'delete': {
              if (!params.key) {
                return this.errorResult('key is required for action=delete');
              }

              const variables: any[] = await this.client.execute(c =>
                (c as any).serverless.v1.services(serviceSid)
                  .environments(envSid)
                  .variables.list()
              );

              const existing = variables.find((v: any) => v.key === params.key);
              if (!existing) {
                return this.errorResult(`Variable not found: ${params.key}`);
              }

              await this.client.execute(c =>
                (c as any).serverless.v1.services(serviceSid)
                  .environments(envSid)
                  .variables(existing.sid)
                  .remove()
              );

              return this.textResult(`Environment variable deleted: ${params.key}`);
            }
          }
          
          return this.errorResult('Invalid action');
        } catch (err: any) {
          return this.errorResult(`Failed to manage environment variables: ${err.message}`);
        }
      },
    });
  }
}
