import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { ClaudeService, TranslationRequest, TranslationResponse } from '../claude/claude.service';
import { TranslationType, TranslationQuality } from '../../generated/prisma/client.js';

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

  constructor(
    private prisma: PrismaService,
    private claudeService: ClaudeService,
  ) {}

  /**
   * Get a translation from cache or translate via Claude
   */
  async getTranslation(
    type: TranslationType,
    englishName: string,
    englishDescription?: string,
    englishEffect?: string,
  ): Promise<TranslationResponse> {
    // Check cache first
    const cached = await this.prisma.translation.findUnique({
      where: { type_englishName: { type, englishName } },
    });

    if (cached && this.isValidTranslation(cached, englishDescription, englishEffect)) {
      this.logger.debug(`Cache hit for ${type}: ${englishName}`);
      return {
        englishName: cached.englishName,
        germanName: cached.germanName,
        germanDescription: cached.germanDescription || undefined,
        germanEffect: cached.germanEffect || undefined,
        translationQuality: cached.quality,
      };
    }

    // Translate via Claude
    this.logger.debug(`Cache miss for ${type}: ${englishName}, calling Claude`);
    const translation = await this.claudeService.translateSingle({
      type: type as TranslationRequest['type'],
      englishName,
      englishDescription,
      englishEffect,
    });

    // Cache the result
    await this.upsertTranslation(type, translation);

    return translation;
  }

  /**
   * Get multiple translations, using cache where possible
   */
  async getTranslationsBatch(
    type: TranslationType,
    items: Array<{ englishName: string; englishDescription?: string; englishEffect?: string }>,
  ): Promise<Map<string, TranslationResponse>> {
    const result = new Map<string, TranslationResponse>();

    if (items.length === 0) {
      return result;
    }

    // Check cache for all items
    const englishNames = items.map(i => i.englishName);
    const cached = await this.prisma.translation.findMany({
      where: {
        type,
        englishName: { in: englishNames },
      },
    });

    const cachedMap = new Map(cached.map(c => [c.englishName, c]));
    const itemsMap = new Map(items.map(i => [i.englishName, i]));
    const toTranslate: TranslationRequest[] = [];

    for (const item of items) {
      const cachedItem = cachedMap.get(item.englishName);
      if (cachedItem && this.isValidTranslation(cachedItem, item.englishDescription, item.englishEffect)) {
        result.set(item.englishName, {
          englishName: cachedItem.englishName,
          germanName: cachedItem.germanName,
          germanDescription: cachedItem.germanDescription || undefined,
          germanEffect: cachedItem.germanEffect || undefined,
          translationQuality: cachedItem.quality,
        });
      } else {
        toTranslate.push({
          type: type as TranslationRequest['type'],
          englishName: item.englishName,
          englishDescription: item.englishDescription,
          englishEffect: item.englishEffect,
        });
      }
    }

    this.logger.log(`${type}: ${cached.length} cached, ${toTranslate.length} to translate`);

    // Translate missing items in batches of 20
    if (toTranslate.length > 0) {
      const batchSize = 20;
      for (let i = 0; i < toTranslate.length; i += batchSize) {
        const batch = toTranslate.slice(i, i + batchSize);
        const translations = await this.claudeService.translateBatch(batch);

        for (const translation of translations) {
          result.set(translation.englishName, translation);
          await this.upsertTranslation(type, translation);
        }
      }
    }

    return result;
  }

  /**
   * Store or update a translation in the cache
   */
  async upsertTranslation(
    type: TranslationType,
    translation: TranslationResponse,
  ): Promise<void> {
    await this.prisma.translation.upsert({
      where: {
        type_englishName: { type, englishName: translation.englishName },
      },
      update: {
        germanName: translation.germanName,
        germanDescription: translation.germanDescription,
        germanEffect: translation.germanEffect,
        quality: translation.translationQuality as TranslationQuality,
        updatedAt: new Date(),
      },
      create: {
        type,
        englishName: translation.englishName,
        germanName: translation.germanName,
        germanDescription: translation.germanDescription,
        germanEffect: translation.germanEffect,
        quality: translation.translationQuality as TranslationQuality,
        translatedBy: 'claude-api',
      },
    });
  }

  /**
   * Get all cached translations of a type
   */
  async getAllByType(type: TranslationType) {
    return this.prisma.translation.findMany({
      where: { type },
      orderBy: { englishName: 'asc' },
    });
  }

  /**
   * Check if a cached translation is valid and complete
   */
  private isValidTranslation(
    cached: { englishName: string; germanName: string; germanDescription?: string | null; germanEffect?: string | null; quality: string },
    requestedDescription?: string,
    requestedEffect?: string,
  ): boolean {
    // LOW quality means the translation failed or API was unavailable
    if (cached.quality === 'LOW') {
      return false;
    }

    // If germanName equals englishName, it wasn't actually translated
    if (cached.germanName === cached.englishName) {
      return false;
    }

    // Check if description is incomplete (truncated)
    if (cached.germanDescription) {
      const desc = cached.germanDescription.trim();
      if (desc.endsWith('…') || desc.endsWith('...')) {
        return false;
      }
    }

    // If a description was requested but not cached, re-translate
    if (requestedDescription && !cached.germanDescription) {
      return false;
    }

    // If an effect was requested but not cached, re-translate
    if (requestedEffect && !cached.germanEffect) {
      return false;
    }

    return true;
  }
}
