/**
 * Schematic generator for behavioral guideline batches.
 *
 * Port of boocontext-audit/src/generation.ts — abstract LLM batch caller
 * with temperature retry and structured output per batch type.
 */

import { type GenerationInfo } from './matching.js';

export interface ObservationalOutput {
  checks: {
    guideline_id: string;
    condition: string;
    rationale: string;
    applies: boolean;
  }[];
}

export interface ActionableOutput {
  checks: {
    guideline_id: string;
    condition: string;
    action: string;
    rationale: string;
    applies: boolean;
  }[];
}

export interface PreviouslyAppliedOutput {
  checks: {
    guideline_id: string;
    condition: string;
    action_segment: string;
    rationale: string;
    is_still_applicable: boolean;
  }[];
}

export interface DisambiguationOutput {
  source_guideline_id: string;
  rationale: string;
  enriched_action: string;
  targets: string[];
}

export interface ResponseAnalysisOutput {
  guideline_id: string;
  condition: string;
  was_followed: boolean;
  rationale: string;
}

export interface BatchOutputMap {
  observational: ObservationalOutput;
  actionable: ActionableOutput;
  previously_applied: PreviouslyAppliedOutput;
  disambiguation: DisambiguationOutput;
  response_analysis: ResponseAnalysisOutput;
}

export type BatchTypeKey = keyof BatchOutputMap;

export type OutputForBatch<T extends BatchTypeKey> = BatchOutputMap[T];

export abstract class SchematicGenerator<TSchema> {
  constructor(public modelName: string) {}

  abstract generate(
    prompt: string,
    hints?: Record<string, unknown>,
  ): Promise<{
    content: TSchema;
    info: GenerationInfo;
  }>;
}

/**
 * Default stub implementation that returns empty results.
 * Replace with a real LLM caller in production.
 */
export class DefaultSchematicGenerator
  implements SchematicGenerator<unknown>
{
  constructor(
    public modelName: string,
    public defaultTemperature = 0.7,
  ) {}

  async generate(
    _prompt: string,
    hints?: Record<string, unknown>,
  ): Promise<{ content: unknown; info: GenerationInfo }> {
    const temperature = (hints?.temperature as number) ?? this.defaultTemperature;
    return {
      content: {},
      info: {
        model: this.modelName,
        duration: 0,
        tokens: 0,
        temperature,
      },
    };
  }
}

export interface BatchExecutionPlan {
  batchType: BatchTypeKey;
  guidelines: { id: string; condition: string; action?: string | null }[];
  priority: number;
  independent: boolean;
}

/**
 * Create an ordered execution plan from categorized guideline collections.
 * Groups are sorted by priority: previously_applied (fastest) first,
 * then observational, actionable, disambiguation, low-criticality last.
 */
export function createExecutionPlan(
  observational: { id: string; condition: string }[],
  actionable: { id: string; condition: string; action: string }[],
  previouslyApplied: { id: string; condition: string; action?: string | null }[],
  disambiguationGroups: { source: string; targets: string[]; enrichedAction: string }[],
  lowCriticality: { id: string; condition: string }[],
): BatchExecutionPlan[] {
  const plans: BatchExecutionPlan[] = [];

  if (observational.length > 0) {
    plans.push({
      batchType: 'observational',
      guidelines: observational.map((g) => ({ id: g.id, condition: g.condition })),
      priority: 1,
      independent: true,
    });
  }

  if (actionable.length > 0) {
    plans.push({
      batchType: 'actionable',
      guidelines: actionable.map((g) => ({
        id: g.id,
        condition: g.condition,
        action: g.action,
      })),
      priority: 2,
      independent: true,
    });
  }

  if (previouslyApplied.length > 0) {
    plans.push({
      batchType: 'previously_applied',
      guidelines: previouslyApplied.map((g) => ({
        id: g.id,
        condition: g.condition,
        action: g.action,
      })),
      priority: 0,
      independent: true,
    });
  }

  if (disambiguationGroups.length > 0) {
    plans.push({
      batchType: 'disambiguation',
      guidelines: disambiguationGroups.map((g) => ({
        id: g.source,
        condition: g.enrichedAction,
      })),
      priority: 3,
      independent: true,
    });
  }

  if (lowCriticality.length > 0) {
    plans.push({
      batchType: 'observational',
      guidelines: lowCriticality.map((g) => ({ id: g.id, condition: g.condition })),
      priority: 10,
      independent: true,
    });
  }

  return plans.sort((a, b) => a.priority - b.priority);
}

/**
 * Compute retry temperatures: base + 0.2 * attempt.
 * Provides progressive temperature increases for failed calls.
 */
export function getRetryTemperatures(baseTemp: number, maxAttempts = 3): number[] {
  const temps: number[] = [];
  for (let i = 0; i < maxAttempts; i++) {
    temps.push(baseTemp + i * 0.2);
  }
  return temps;
}
