import { Injectable, NotFoundException, BadRequestException, Logger } from '@nestjs/common';
import { PrismaService } from '@chatapp/database';
import { ConfigService } from '@nestjs/config';
import * as crypto from 'crypto';

@Injectable()
export class IntegrationsService {
  private readonly logger = new Logger(IntegrationsService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly config: ConfigService,
  ) {}

  async listApps(workspaceId: string) {
    return this.prisma.app.findMany({
      where: { workspaceId, isActive: true },
      select: { id: true, appName: true, appSlug: true, iconUrl: true, developerName: true, scopes: true, installedAt: true },
    });
  }

  async installApp(workspaceId: string, userId: string, appSlug: string, scopes: string[]) {
    const existing = await this.prisma.app.findFirst({ where: { workspaceId, appSlug, isActive: true } });
    if (existing) throw new BadRequestException('App already installed');
    return this.prisma.app.create({
      data: { workspaceId, appName: appSlug, appSlug, scopes, installedBy: userId, isActive: true, config: {} },
    });
  }

  async uninstallApp(workspaceId: string, appId: string) {
    const app = await this.prisma.app.findUnique({ where: { id: appId } });
    if (!app || app.workspaceId !== workspaceId) throw new NotFoundException('App not found');
    return this.prisma.app.update({ where: { id: appId }, data: { isActive: false } });
  }

  async listSlashCommands(workspaceId: string) {
    return this.prisma.slashCommand.findMany({ where: { workspaceId } });
  }

  async createSlashCommand(workspaceId: string, _userId: string, data: { command: string; url: string; description?: string; usageHint?: string }) {
    return this.prisma.slashCommand.create({
      data: { workspaceId, command: data.command, url: data.url, description: data.description || null, usageHint: data.usageHint || null },
    });
  }

  async executeSlashCommand(workspaceId: string, _userId: string, command: string, text: string, channelId: string) {
    const cmd = await this.prisma.slashCommand.findFirst({ where: { workspaceId, command } });
    if (!cmd) throw new NotFoundException(`Command ${command} not found`);
    return { command: cmd.command, text, channelId, response_type: 'ephemeral', message: `Command ${cmd.command} executed: ${text}` };
  }

  async createWebhook(workspaceId: string, userId: string, channelId: string, _name: string) {
    const token = crypto.randomBytes(32).toString('hex');
    const webhookUrl = `http://localhost:3010/api/v1/hooks/${token}`;
    return this.prisma.incomingWebhook.create({
      data: { workspaceId, channelId, webhookUrl, webhookToken: token, createdBy: userId, isActive: true },
    });
  }

  async listWebhooks(workspaceId: string) {
    return this.prisma.incomingWebhook.findMany({ where: { workspaceId, isActive: true } });
  }

  async handleWebhook(token: string, body: { text?: string; blocks?: any[]; channel?: string }) {
    const webhook = await this.prisma.incomingWebhook.findFirst({ where: { webhookToken: token, isActive: true } });
    if (!webhook) throw new NotFoundException('Webhook not found');
    const channelId = body.channel || webhook.channelId || '';
    const message = await this.prisma.message.create({
      data: { workspaceId: webhook.workspaceId, channelId, text: body.text || '', blocks: (body.blocks as any) || undefined },
    });
    return { ok: true, messageId: message.id };
  }

  async listEventSubscriptions(workspaceId: string) {
    return this.prisma.eventSubscription.findMany({ where: { workspaceId, isActive: true } });
  }

  async createEventSubscription(workspaceId: string, data: { callbackUrl: string; eventTypes: string[] }) {
    const secret = crypto.randomBytes(32).toString('hex');
    return this.prisma.eventSubscription.create({
      data: { workspaceId, callbackUrl: data.callbackUrl, eventTypes: data.eventTypes, secret, isActive: true },
    });
  }

  async listWorkflows(workspaceId: string) {
    return this.prisma.workflow.findMany({ where: { workspaceId }, include: { _count: { select: { executions: true } } } });
  }

  async createWorkflow(workspaceId: string, userId: string, data: { name: string; description?: string; triggerType?: string; triggerConfig?: any; steps?: any[] }) {
    return this.prisma.workflow.create({
      data: {
        workspaceId, name: data.name, description: data.description || null,
        triggerType: data.triggerType || 'manual', triggerConfig: data.triggerConfig || {},
        steps: data.steps || [], createdBy: userId, status: 'active',
      },
    });
  }

  async executeWorkflow(workflowId: string, context: any) {
    const workflow = await this.prisma.workflow.findUnique({ where: { id: workflowId } });
    if (!workflow || workflow.status !== 'active') throw new NotFoundException('Workflow not found or inactive');

    const execution = await this.prisma.workflowExecution.create({
      data: { workflowId, status: 'running', triggerData: context, startedAt: new Date() },
    });

    try {
      const steps = workflow.steps as any[];
      const results: any[] = [];
      for (const step of steps) {
        const result = await this.executeStep(step, context, workflow.workspaceId);
        results.push(result);
      }
      await this.prisma.workflowExecution.update({
        where: { id: execution.id },
        data: { status: 'completed', context: { steps: results } as any, completedAt: new Date() },
      });
      await this.prisma.workflow.update({
        where: { id: workflowId },
        data: { executionCount: { increment: 1 }, lastExecuted: new Date() },
      });
      return { executionId: execution.id, status: 'completed', results };
    } catch (err: any) {
      await this.prisma.workflowExecution.update({
        where: { id: execution.id },
        data: { status: 'failed', errorMessage: err.message, completedAt: new Date() },
      });
      await this.prisma.workflow.update({
        where: { id: workflowId },
        data: { failureCount: { increment: 1 } },
      });
      throw err;
    }
  }

  private async executeStep(step: any, context: any, workspaceId: string): Promise<any> {
    switch (step.type) {
      case 'send_message':
        return this.prisma.message.create({
          data: { workspaceId, channelId: step.channelId, text: this.interpolateTemplate(step.text || '', context), isSystem: true },
        });
      case 'create_channel': {
        const name = step.name || 'new-channel';
        const handle = name.toLowerCase().replace(/\s+/g, '-');
        return this.prisma.channel.create({
          data: { workspaceId, name, handle, type: step.isPrivate ? 'private' : 'public', createdBy: context.userId || '' },
        });
      }
      case 'http_call':
        this.logger.log(`HTTP call to ${step.url} (skipped in dev)`);
        return { url: step.url, skipped: true };
      default:
        return { error: `Unknown step type: ${step.type}` };
    }
  }

  private interpolateTemplate(template: string, context: any): string {
    return template.replace(/\{\{(\w+)\}\}/g, (_, key) => context[key] || '');
  }

  async updateWorkflow(workflowId: string, wsId: string, data: { name?: string; description?: string; trigger_type?: string; trigger_config?: any; steps?: any[] }) {
    const workflow = await this.prisma.workflow.findFirst({ where: { id: workflowId, workspaceId: wsId } });
    if (!workflow) throw new NotFoundException('Workflow not found');

    const updateData: any = {};
    if (data.name !== undefined) updateData.name = data.name;
    if (data.description !== undefined) updateData.description = data.description;
    if (data.trigger_type !== undefined) updateData.triggerType = data.trigger_type;
    if (data.trigger_config !== undefined) updateData.triggerConfig = data.trigger_config;
    if (data.steps !== undefined) updateData.steps = data.steps;

    return this.prisma.workflow.update({
      where: { id: workflowId },
      data: updateData,
    });
  }

  async deleteWorkflow(workflowId: string, wsId: string) {
    const workflow = await this.prisma.workflow.findFirst({ where: { id: workflowId, workspaceId: wsId } });
    if (!workflow) throw new NotFoundException('Workflow not found');

    await this.prisma.workflow.delete({ where: { id: workflowId } });
    return { deleted: true };
  }

  async toggleWorkflow(workflowId: string, wsId: string) {
    const workflow = await this.prisma.workflow.findFirst({ where: { id: workflowId, workspaceId: wsId } });
    if (!workflow) throw new NotFoundException('Workflow not found');

    const newStatus = workflow.status === 'active' ? 'draft' : 'active';
    const updated = await this.prisma.workflow.update({
      where: { id: workflowId },
      data: { status: newStatus },
    });
    return { id: updated.id, status: updated.status };
  }
}