import { Injectable } from "@nestjs/common";
import { ContinuationPromptContext, FailureDetail, ContinuationPrompt } from "./interfaces";
import {
  BASE_CONTINUATION_SYSTEM,
  BASE_USER_PROMPT,
  TEST_FAILURE_TEMPLATE,
  BUILD_ERROR_TEMPLATE,
  COVERAGE_TEMPLATE,
  INCOMPLETE_WORK_TEMPLATE,
} from "./templates";

/**
 * Service for generating continuation prompts when AI agent work is incomplete
 */
@Injectable()
export class ContinuationPromptsService {
  /**
   * Generate a complete continuation prompt from context
   */
  generatePrompt(context: ContinuationPromptContext): ContinuationPrompt {
    const systemPrompt = BASE_CONTINUATION_SYSTEM;
    const constraints = this.getConstraints(context.attemptNumber, context.maxAttempts);

    // Format failures based on their types
    const formattedFailures = this.formatFailuresByType(context.failures);

    // Build user prompt
    const userPrompt = BASE_USER_PROMPT.replace("{{taskDescription}}", context.originalTask)
      .replace("{{attemptNumber}}", String(context.attemptNumber))
      .replace("{{maxAttempts}}", String(context.maxAttempts))
      .replace("{{failures}}", formattedFailures)
      .replace("{{constraints}}", this.formatConstraints(constraints));

    // Determine priority
    const priority = this.determinePriority(context);

    return {
      systemPrompt,
      userPrompt,
      constraints,
      priority,
    };
  }

  /**
   * Generate test failure specific prompt
   */
  generateTestFailurePrompt(failures: FailureDetail[]): string {
    const formattedFailures = this.formatFailuresForPrompt(failures);
    return TEST_FAILURE_TEMPLATE.replace("{{failures}}", formattedFailures);
  }

  /**
   * Generate build error specific prompt
   */
  generateBuildErrorPrompt(failures: FailureDetail[]): string {
    const formattedErrors = this.formatFailuresForPrompt(failures);
    return BUILD_ERROR_TEMPLATE.replace("{{errors}}", formattedErrors);
  }

  /**
   * Generate coverage improvement prompt
   */
  generateCoveragePrompt(current: number, required: number): string {
    const gap = required - current;
    return COVERAGE_TEMPLATE.replace("{{currentCoverage}}", String(current))
      .replace("{{requiredCoverage}}", String(required))
      .replace("{{gap}}", String(gap))
      .replace("{{uncoveredFiles}}", "(See coverage report for details)");
  }

  /**
   * Generate incomplete work prompt
   */
  generateIncompleteWorkPrompt(issues: string[]): string {
    const formattedIssues = issues.map((issue) => `- ${issue}`).join("\n");
    return INCOMPLETE_WORK_TEMPLATE.replace("{{issues}}", formattedIssues);
  }

  /**
   * Get constraints based on attempt number
   */
  getConstraints(attemptNumber: number, maxAttempts: number): string[] {
    const constraints: string[] = [
      "Address ALL failures listed above",
      "Run all quality checks before claiming completion",
    ];

    if (attemptNumber >= 2) {
      constraints.push("Focus only on failures, no new features");
    }

    if (attemptNumber >= 3) {
      constraints.push("Minimal changes only, fix exact issues");
    }

    if (attemptNumber >= maxAttempts) {
      constraints.push("This is your LAST attempt. Failure means manual intervention required.");
    }

    return constraints;
  }

  /**
   * Format failures for inclusion in prompt
   */
  formatFailuresForPrompt(failures: FailureDetail[]): string {
    if (failures.length === 0) {
      return "";
    }

    return failures
      .map((failure, index) => {
        const parts: string[] = [`${String(index + 1)}. [${failure.type}] ${failure.message}`];

        if (failure.location) {
          parts.push(`   Location: ${failure.location}`);
        }

        if (failure.details) {
          parts.push(`   Details: ${failure.details}`);
        }

        if (failure.suggestion) {
          parts.push(`   Suggestion: ${failure.suggestion}`);
        }

        return parts.join("\n");
      })
      .join("\n\n");
  }

  /**
   * Format failures by type using appropriate templates
   */
  private formatFailuresByType(failures: FailureDetail[]): string {
    const sections: string[] = [];

    // Group failures by type
    const testFailures = failures.filter((f) => f.type === "test-failure");
    const buildErrors = failures.filter((f) => f.type === "build-error");
    const coverageIssues = failures.filter((f) => f.type === "coverage");
    const incompleteWork = failures.filter((f) => f.type === "incomplete-work");
    const lintErrors = failures.filter((f) => f.type === "lint-error");

    if (testFailures.length > 0) {
      sections.push(this.generateTestFailurePrompt(testFailures));
    }

    if (buildErrors.length > 0) {
      sections.push(this.generateBuildErrorPrompt(buildErrors));
    }

    if (coverageIssues.length > 0) {
      // Extract coverage numbers from message if available
      const coverageFailure = coverageIssues[0];
      if (coverageFailure) {
        const match = /(\d+)%.*?(\d+)%/.exec(coverageFailure.message);
        if (match?.[1] && match[2]) {
          sections.push(this.generateCoveragePrompt(parseInt(match[1]), parseInt(match[2])));
        } else {
          sections.push(this.formatFailuresForPrompt(coverageIssues));
        }
      }
    }

    if (incompleteWork.length > 0) {
      const issues = incompleteWork.map((f) => f.message);
      sections.push(this.generateIncompleteWorkPrompt(issues));
    }

    if (lintErrors.length > 0) {
      sections.push("Lint Errors:\n" + this.formatFailuresForPrompt(lintErrors));
    }

    return sections.join("\n\n---\n\n");
  }

  /**
   * Format constraints as a bulleted list
   */
  private formatConstraints(constraints: string[]): string {
    return "CONSTRAINTS:\n" + constraints.map((c) => `- ${c}`).join("\n");
  }

  /**
   * Determine priority based on context
   */
  private determinePriority(context: ContinuationPromptContext): "critical" | "high" | "normal" {
    // Final attempt is always critical
    if (context.attemptNumber >= context.maxAttempts) {
      return "critical";
    }

    // Build errors and test failures are high priority
    const hasCriticalFailures = context.failures.some(
      (f) => f.type === "build-error" || f.type === "test-failure"
    );

    if (hasCriticalFailures) {
      return "high";
    }

    // Everything else is normal
    return "normal";
  }
}
