import { Injectable, Logger } from "@nestjs/common";
import { PrismaService } from "../prisma/prisma.service";
import { BullMqService } from "../bullmq/bullmq.service";
import { QUEUE_NAMES } from "../bullmq/queues";
import type {
  JobDispatchContext,
  JobDispatchResult,
  GuardRailsResult,
  QualityRailsResult,
} from "./interfaces";
import type { WebhookPayloadDto } from "./dto";

/**
 * StitcherService - Workflow orchestration layer that wraps OpenClaw
 *
 * Responsibilities:
 * - Receive webhooks from @mosaic bot
 * - Apply Guard Rails (capability permissions)
 * - Apply Quality Rails (mandatory gates)
 * - Track all job steps and events
 * - Dispatch work to OpenClaw with constraints
 */
@Injectable()
export class StitcherService {
  private readonly logger = new Logger(StitcherService.name);

  constructor(
    private readonly prisma: PrismaService,
    private readonly bullMq: BullMqService
  ) {}

  /**
   * Handle webhook from @mosaic bot
   */
  async handleWebhook(payload: WebhookPayloadDto): Promise<JobDispatchResult> {
    this.logger.log(
      `Webhook received: ${payload.action} on ${payload.repository}#${payload.issueNumber}`
    );

    // For now, create a simple job dispatch context
    // In the future, this will query workspace info and determine job type
    const context: JobDispatchContext = {
      workspaceId: "default-workspace", // TODO: Determine from repository
      type: "code-task",
      priority: 10,
      metadata: {
        issueNumber: payload.issueNumber,
        repository: payload.repository,
        action: payload.action,
        comment: payload.comment,
      },
    };

    return this.dispatchJob(context);
  }

  /**
   * Dispatch a job to the queue with guard rails and quality rails applied
   */
  async dispatchJob(context: JobDispatchContext): Promise<JobDispatchResult> {
    const { workspaceId, type, priority = 5, metadata } = context;

    this.logger.log(`Dispatching job: ${type} for workspace ${workspaceId}`);

    // Create RunnerJob in database
    const job = await this.prisma.runnerJob.create({
      data: {
        workspaceId,
        type,
        priority,
        status: "PENDING",
        progressPercent: 0,
      },
    });

    // Log job creation event
    await this.trackJobEvent(job.id, "job.created", "stitcher", {
      type,
      priority,
      metadata,
    });

    // Dispatch to BullMQ queue
    await this.bullMq.addJob(
      QUEUE_NAMES.MAIN,
      type,
      {
        jobId: job.id,
        workspaceId,
        type,
        metadata,
      },
      {
        priority,
      }
    );

    // Log job queued event
    await this.trackJobEvent(job.id, "job.queued", "stitcher", {
      queueName: QUEUE_NAMES.MAIN,
    });

    this.logger.log(`Job ${job.id} dispatched to ${QUEUE_NAMES.MAIN}`);

    return {
      jobId: job.id,
      queueName: QUEUE_NAMES.MAIN,
      status: job.status,
    };
  }

  /**
   * Apply Guard Rails - capability permission check
   */
  applyGuardRails(agentProfile: string, capabilities: string[]): GuardRailsResult {
    // Define allowed capabilities per agent profile
    const allowedCapabilities: Record<string, string[]> = {
      runner: ["read", "fetch", "query"],
      weaver: ["read", "write", "commit"],
      inspector: ["read", "validate", "gate"],
      herald: ["read", "report", "notify"],
    };

    const allowed = allowedCapabilities[agentProfile] ?? [];
    const hasPermission = capabilities.every((cap) => allowed.includes(cap));

    if (hasPermission) {
      return {
        allowed: true,
      };
    }

    const requiredCap = capabilities.find((cap) => !allowed.includes(cap));
    const result: GuardRailsResult = {
      allowed: false,
      reason: `Profile ${agentProfile} not allowed capabilities: ${capabilities.join(", ")}`,
    };

    if (requiredCap !== undefined) {
      result.requiredCapability = requiredCap;
    }

    return result;
  }

  /**
   * Apply Quality Rails - determine mandatory gates for job type
   */
  applyQualityRails(jobType: string): QualityRailsResult {
    // Code tasks require full quality gates
    if (jobType === "code-task") {
      return {
        required: true,
        gates: ["lint", "typecheck", "test", "coverage"],
      };
    }

    // Read-only tasks don't require gates
    if (jobType === "git-status" || jobType === "priority-calc") {
      return {
        required: false,
        gates: [],
        skipReason: "Read-only task - no quality gates required",
      };
    }

    // Default: basic gates
    return {
      required: true,
      gates: ["lint", "typecheck"],
    };
  }

  /**
   * Track job event in database
   */
  async trackJobEvent(
    jobId: string,
    type: string,
    actor: string,
    payload: Record<string, unknown>
  ): Promise<void> {
    await this.prisma.jobEvent.create({
      data: {
        jobId,
        type,
        actor,
        timestamp: new Date(),
        payload: payload as object,
      },
    });
  }
}
