import { config } from '../config.js';
import { placeOntology } from '../ontology/place-ontology.js';
import { prisma } from '../prisma.js';

type OpenRouterResponse = {
  choices?: Array<{
    finish_reason?: string;
    message?: {
      content?: string;
    };
  }>;
  error?: {
    message?: string;
  };
};

type PlaceAnalysis = {
  placeName: string;
  tags: string[];
  signals: Array<{
    axis: string;
    leaf: string;
    evidence: string;
    confidence: number;
  }>;
  summary: string;
};

type RecordingContext = {
  promptHintsShown: string[];
  recordingPayload: unknown;
};

export async function analyzeVoiceExperience(experienceId: string) {
  const experience = await prisma.voiceExperience.findUnique({
    where: { id: experienceId },
    include: { place: true },
  });

  if (!experience) {
    throw new Error(`Voice experience ${experienceId} was not found.`);
  }

  if (!experience.transcript) {
    throw new Error(`Voice experience ${experienceId} has no transcript.`);
  }

  await prisma.voiceExperience.update({
    where: { id: experienceId },
    data: { status: 'ANALYZING' },
  });

  const analysis = await buildPlaceAnalysis({
    placeName: experience.place.name,
    transcript: experience.transcript,
    promptHintsShown: experience.promptHintsShown,
    recordingPayload: experience.recordingPayload,
  });

  await prisma.voiceExperience.update({
    where: { id: experienceId },
    data: {
      analysis,
      status: 'ANALYZED',
    },
  });

  return { tags: analysis.tags };
}

async function buildPlaceAnalysis(input: {
  placeName: string;
  transcript: string;
  promptHintsShown: string[];
  recordingPayload: unknown;
}): Promise<PlaceAnalysis> {
  if (!config.openRouterApiKey) {
    throw new Error('OPENROUTER_API_KEY is required for voice analysis.');
  }

  const ontology = placeOntology.map((axis) => ({
    axis: axis.id,
    options: axis.leaves.map((leaf) => ({
      tag: `${axis.id}:${leaf.id}`,
      keywords: leaf.keywords,
    })),
  }));
  const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
    method: 'POST',
    headers: {
      authorization: `Bearer ${config.openRouterApiKey}`,
      'content-type': 'application/json',
      'http-referer': 'https://map.craftee.vn',
      'x-title': 'MapFlow',
    },
    body: JSON.stringify({
      model: config.openRouterModel,
      provider: { require_parameters: true },
      temperature: 0.1,
      max_tokens: 2000,
      response_format: {
        type: 'json_schema',
        json_schema: {
          name: 'place_analysis',
          strict: true,
          schema: {
            type: 'object',
            additionalProperties: false,
            required: ['placeName', 'tags', 'signals', 'summary'],
            properties: {
              placeName: { type: 'string' },
              tags: { type: 'array', items: { type: 'string' } },
              signals: {
                type: 'array',
                items: {
                  type: 'object',
                  additionalProperties: false,
                  required: ['axis', 'leaf', 'evidence', 'confidence'],
                  properties: {
                    axis: { type: 'string' },
                    leaf: { type: 'string' },
                    evidence: { type: 'string' },
                    confidence: { type: 'number', minimum: 0, maximum: 1 },
                  },
                },
              },
              summary: { type: 'string' },
            },
          },
        },
      },
      messages: [
        {
          role: 'system',
          content:
            'You classify voice reviews about places. Return one valid JSON object in message.content. Do not return markdown. Use only allowed ontology tags.',
        },
        {
          role: 'user',
          content: JSON.stringify({
            task:
              'Analyze the transcript and select place-experience tags that are explicitly supported by the text. Do not invent facts.',
            outputShape: {
              placeName: 'string',
              tags: ['axis:leaf'],
              signals: [
                {
                  axis: 'string',
                  leaf: 'string',
                  evidence: 'short quote or paraphrase from transcript',
                  confidence: 'number from 0 to 1',
                },
              ],
              summary: 'one short sentence',
            },
            placeName: input.placeName,
            ontology,
            recordingContext: recordingContext(input),
            transcript: input.transcript,
          }),
        },
      ],
    }),
  });

  if (!response.ok) {
    const errorBody = await response.text();
    throw new Error(
      `OpenRouter analysis failed with ${response.status}: ${errorBody.slice(0, 500)}`,
    );
  }

  const payload = (await response.json()) as OpenRouterResponse;
  const choice = payload.choices?.[0];
  const content = choice?.message?.content?.trim();
  if (!content) {
    throw new Error(
      `OpenRouter returned an empty analysis for ${config.openRouterModel}; finish_reason=${choice?.finish_reason ?? 'missing'}.`,
    );
  }

  const analysis = JSON.parse(content) as PlaceAnalysis;
  assertValidAnalysis(analysis);
  return analysis;
}

function recordingContext(input: RecordingContext) {
  return {
    promptHintsShown: input.promptHintsShown,
    recordingPayload: input.recordingPayload,
  };
}

function assertValidAnalysis(analysis: PlaceAnalysis) {
  const allowedTags = new Set(
    placeOntology.flatMap((axis) =>
      axis.leaves.map((leaf) => `${axis.id}:${leaf.id}`),
    ),
  );

  if (!Array.isArray(analysis.tags)) {
    throw new Error('OpenRouter analysis tags must be an array.');
  }
  for (const tag of analysis.tags) {
    if (!allowedTags.has(tag)) {
      throw new Error(`OpenRouter returned unsupported tag: ${tag}.`);
    }
  }
  if (!Array.isArray(analysis.signals)) {
    throw new Error('OpenRouter analysis signals must be an array.');
  }
}
