/**
 * Component: Interactive Search Ebook API
 * Documentation: documentation/integrations/ebook-sidecar.md
 *
 * Searches for ebooks from multiple sources (Anna's Archive + Indexers)
 * Returns combined results for user selection in interactive modal
 */

import { NextRequest, NextResponse } from 'next/server';
import { requireAuth, requireAdmin, AuthenticatedRequest } from '@/lib/middleware/auth';
import { prisma } from '@/lib/db';
import { getConfigService } from '@/lib/services/config.service';
import { getProwlarrService } from '@/lib/integrations/prowlarr.service';
import { rankEbookTorrents, RankedEbookTorrent } from '@/lib/utils/ranking-algorithm';
import { groupIndexersByCategories, getGroupDescription } from '@/lib/utils/indexer-grouping';
import { RMABLogger } from '@/lib/utils/logger';
import { getLanguageForRegion } from '@/lib/constants/language-config';
import type { AudibleRegion } from '@/lib/types/audible';
import {
  searchByAsin,
  searchByTitle,
  getSlowDownloadLinks,
} from '@/lib/services/ebook-scraper';

const logger = RMABLogger.create('API.InteractiveSearchEbook');

// Unified result type for frontend
export interface EbookSearchResult {
  // Common fields (match RankedTorrent shape for UI compatibility)
  guid: string;
  title: string;
  size: number;
  seeders?: number;
  indexer: string;
  indexerId?: number;
  publishDate: Date;
  downloadUrl: string;
  infoUrl?: string;
  protocol?: string; // 'torrent' or 'usenet' - determines download client

  // Ranking fields
  score: number;
  finalScore: number;
  bonusPoints: number;
  bonusModifiers: Array<{ type: string; value: number; points: number; reason: string }>;
  rank: number;
  breakdown: {
    formatScore: number;
    sizeScore: number;
    seederScore: number;
    matchScore: number;
    totalScore: number;
    notes: string[];
  };

  // Ebook-specific fields
  source: 'annas_archive' | 'prowlarr';
  format?: string;
  md5?: string;
  downloadUrls?: string[];
}

export async function POST(
  request: NextRequest,
  { params }: { params: Promise<{ id: string }> }
) {
  return requireAuth(request, async (req: AuthenticatedRequest) => {
    return requireAdmin(req, async () => {
      try {
        const { id: parentRequestId } = await params;
        const body = await request.json().catch(() => ({}));
        const customTitle = body.customTitle as string | undefined;

        // Get the request (can be audiobook parent or direct ebook request)
        const requestRecord = await prisma.request.findUnique({
          where: { id: parentRequestId },
          include: { audiobook: true },
        });

        if (!requestRecord) {
          return NextResponse.json({ error: 'Request not found' }, { status: 404 });
        }

        // Support two flows:
        // Flow A (sidecar): Audiobook request in downloaded/available state
        // Flow B (direct):  Ebook request in pending/failed/awaiting_search state
        const isDirectEbookSearch = requestRecord.type === 'ebook';
        const isAudiobookSidecar = requestRecord.type === 'audiobook';

        if (!isDirectEbookSearch && !isAudiobookSidecar) {
          return NextResponse.json({ error: 'Invalid request type' }, { status: 400 });
        }

        if (isAudiobookSidecar && !['downloaded', 'available'].includes(requestRecord.status)) {
          return NextResponse.json(
            { error: `Cannot search ebooks for audiobook request in ${requestRecord.status} status` },
            { status: 400 }
          );
        }

        if (isDirectEbookSearch && !['pending', 'failed', 'awaiting_search'].includes(requestRecord.status)) {
          return NextResponse.json(
            { error: `Cannot search for ebook request in ${requestRecord.status} status` },
            { status: 400 }
          );
        }

        // Check for existing child ebook requests (sidecar mode only)
        if (isAudiobookSidecar) {
          const existingEbookRequest = await prisma.request.findFirst({
            where: {
              parentRequestId,
              type: 'ebook',
              deletedAt: null,
            },
          });

          if (existingEbookRequest && !['failed', 'awaiting_search'].includes(existingEbookRequest.status)) {
            return NextResponse.json({
              error: `E-book request already exists (status: ${existingEbookRequest.status})`,
              existingRequestId: existingEbookRequest.id,
            }, { status: 400 });
          }
        }

        // Get ebook configuration
        const configService = getConfigService();
        const [annasArchiveEnabled, indexerSearchEnabled, preferredFormat, baseUrl, flaresolverrUrl] = await Promise.all([
          configService.get('ebook_annas_archive_enabled'),
          configService.get('ebook_indexer_search_enabled'),
          configService.get('ebook_sidecar_preferred_format'),
          configService.get('ebook_sidecar_base_url'),
          configService.get('ebook_sidecar_flaresolverr_url'),
        ]);

        const isAnnasArchiveEnabled = annasArchiveEnabled === 'true';
        const isIndexerSearchEnabled = indexerSearchEnabled === 'true';
        const format = preferredFormat || 'epub';
        const annasBaseUrl = baseUrl || 'https://annas-archive.gl';

        // Get language code from Audible region config
        const region = await configService.getAudibleRegion() as AudibleRegion;
        const langConfig = getLanguageForRegion(region);
        const languageCode = langConfig.annasArchiveLang;

        if (!isAnnasArchiveEnabled && !isIndexerSearchEnabled) {
          return NextResponse.json(
            { error: 'No ebook sources enabled. Enable Anna\'s Archive or Indexer Search in settings.' },
            { status: 400 }
          );
        }

        const audiobook = requestRecord.audiobook;
        const searchTitle = customTitle || audiobook.title;

        logger.info(`Interactive ebook search for "${searchTitle}" by ${audiobook.author} (${isDirectEbookSearch ? 'direct' : 'sidecar'})`);
        logger.info(`Sources: Anna's Archive=${isAnnasArchiveEnabled}, Indexer=${isIndexerSearchEnabled}`);

        // Search both sources in parallel
        const searchPromises: Promise<EbookSearchResult[] | null>[] = [];

        if (isAnnasArchiveEnabled) {
          searchPromises.push(
            searchAnnasArchiveForInteractive(
              audiobook.audibleAsin || undefined,
              searchTitle,
              audiobook.author,
              format,
              annasBaseUrl,
              flaresolverrUrl || undefined,
              languageCode
            ).catch((err) => {
              logger.error(`Anna's Archive search failed: ${err.message}`);
              return null;
            })
          );
        }

        if (isIndexerSearchEnabled) {
          searchPromises.push(
            searchIndexersForInteractive(
              searchTitle,
              audiobook.author,
              format
            ).catch((err) => {
              logger.error(`Indexer search failed: ${err.message}`);
              return null;
            })
          );
        }

        const searchResults = await Promise.all(searchPromises);

        // Combine results: Anna's Archive first (if found), then ranked indexer results
        const combinedResults: EbookSearchResult[] = [];
        let rank = 1;

        // Add Anna's Archive result first (if enabled and found)
        if (isAnnasArchiveEnabled && searchResults[0]) {
          const annasResults = searchResults[0];
          for (const result of annasResults) {
            combinedResults.push({ ...result, rank: rank++ });
          }
        }

        // Add indexer results (already ranked)
        const indexerResultsIndex = isAnnasArchiveEnabled ? 1 : 0;
        if (isIndexerSearchEnabled && searchResults[indexerResultsIndex]) {
          const indexerResults = searchResults[indexerResultsIndex];
          for (const result of indexerResults) {
            combinedResults.push({ ...result, rank: rank++ });
          }
        }

        logger.info(`Found ${combinedResults.length} total ebook results`);

        return NextResponse.json({
          results: combinedResults,
          searchTitle,
          preferredFormat: format,
        });

      } catch (error) {
        logger.error('Unexpected error', { error: error instanceof Error ? error.message : String(error) });
        return NextResponse.json(
          { error: error instanceof Error ? error.message : 'Internal server error' },
          { status: 500 }
        );
      }
    });
  });
}

/**
 * Search Anna's Archive and return normalized results
 */
async function searchAnnasArchiveForInteractive(
  asin: string | undefined,
  title: string,
  author: string,
  preferredFormat: string,
  baseUrl: string,
  flaresolverrUrl?: string,
  languageCode: string = 'en'
): Promise<EbookSearchResult[]> {
  let md5: string | null = null;
  let searchMethod: 'asin' | 'title' = 'title';

  // Try ASIN search first
  if (asin) {
    logger.info(`Searching Anna's Archive by ASIN: ${asin}`);
    md5 = await searchByAsin(asin, preferredFormat, baseUrl, undefined, flaresolverrUrl, languageCode);
    if (md5) {
      searchMethod = 'asin';
      logger.info(`Found via ASIN: ${md5}`);
    }
  }

  // Fallback to title search
  if (!md5) {
    logger.info(`Searching Anna's Archive by title: "${title}"`);
    md5 = await searchByTitle(title, author, preferredFormat, baseUrl, undefined, flaresolverrUrl, languageCode);
    if (md5) {
      logger.info(`Found via title: ${md5}`);
    }
  }

  if (!md5) {
    logger.info('No results from Anna\'s Archive');
    return [];
  }

  // Get download links
  const slowLinks = await getSlowDownloadLinks(md5, baseUrl, undefined, flaresolverrUrl);

  if (slowLinks.length === 0) {
    logger.warn(`Found MD5 ${md5} but no download links available`);
    return [];
  }

  // Return as normalized result - always score 100 for Anna's Archive
  const score = 100;

  return [{
    guid: `annas-archive-${md5}`,
    title: `${title} - ${author}`,
    size: 0, // Unknown until download
    seeders: 999, // N/A for direct download, use high number for display
    indexer: "Anna's Archive",
    publishDate: new Date(),
    downloadUrl: slowLinks[0],
    infoUrl: `${baseUrl}/md5/${md5}`,

    score,
    finalScore: score,
    bonusPoints: 0,
    bonusModifiers: [],
    rank: 1,
    breakdown: {
      formatScore: 10,
      sizeScore: 15,
      seederScore: 15,
      matchScore: 60,
      totalScore: score,
      notes: [searchMethod === 'asin' ? 'ASIN match' : 'Title/Author match', "Anna's Archive"],
    },

    source: 'annas_archive',
    format: preferredFormat,
    md5,
    downloadUrls: slowLinks,
  }];
}

/**
 * Search indexers and return ranked results
 */
async function searchIndexersForInteractive(
  title: string,
  author: string,
  preferredFormat: string
): Promise<EbookSearchResult[]> {
  const configService = getConfigService();

  // Get indexer configuration
  const indexersConfigStr = await configService.get('prowlarr_indexers');
  if (!indexersConfigStr) {
    logger.warn('No indexers configured');
    return [];
  }

  const indexersConfig = JSON.parse(indexersConfigStr);
  if (indexersConfig.length === 0) {
    logger.warn('No indexers enabled');
    return [];
  }

  // Build indexer priorities map
  const indexerPriorities = new Map<number, number>(
    indexersConfig.map((indexer: any) => [indexer.id, indexer.priority ?? 10])
  );

  // Get flag configurations
  const flagConfigStr = await configService.get('indexer_flag_config');
  const flagConfigs = flagConfigStr ? JSON.parse(flagConfigStr) : [];

  // Group indexers by ebook categories
  const { groups, skippedIndexers } = groupIndexersByCategories(indexersConfig, 'ebook');

  if (skippedIndexers.length > 0) {
    const skippedNames = skippedIndexers.map(idx => idx.name).join(', ');
    logger.info(`Skipping ${skippedIndexers.length} indexer(s) with no ebook categories: ${skippedNames}`);
  }

  logger.info(`Searching ${indexersConfig.length - skippedIndexers.length} indexers in ${groups.length} group(s)`);

  // Get Prowlarr service
  const prowlarr = await getProwlarrService();

  // Search each group and combine results
  const allResults = [];

  for (const group of groups) {
    try {
      const groupResults = await prowlarr.search(title, {
        categories: group.categories,
        indexerIds: group.indexerIds,
        minSeeders: 0,
        maxResults: 100,
      });
      allResults.push(...groupResults);
    } catch (error) {
      logger.error(`Group search failed: ${error instanceof Error ? error.message : 'Unknown'}`);
    }
  }

  logger.info(`Found ${allResults.length} results from indexers`);

  if (allResults.length === 0) {
    return [];
  }

  // Get language-specific stop words for ranking
  const rankRegion = await configService.getAudibleRegion() as AudibleRegion;
  const rankLangConfig = getLanguageForRegion(rankRegion);

  // Rank results with ebook scoring
  // Use requireAuthor=false for interactive mode (let user decide)
  const rankedResults = rankEbookTorrents(allResults, {
    title,
    author,
    preferredFormat,
  }, {
    indexerPriorities,
    flagConfigs,
    requireAuthor: false,
    stopWords: rankLangConfig.stopWords,
    characterReplacements: rankLangConfig.characterReplacements,
  });

  // Log ranking debug info (same format as search-ebook.processor.ts)
  if (rankedResults.length > 0) {
    const top3 = rankedResults.slice(0, 3);
    logger.info(`==================== EBOOK INTERACTIVE SEARCH DEBUG ====================`);
    logger.info(`Requested Title: "${title}"`);
    logger.info(`Requested Author: "${author}"`);
    logger.info(`Preferred Format: ${preferredFormat}`);
    logger.info(`Top ${top3.length} results (out of ${rankedResults.length} total):`);
    logger.info(`--------------------------------------------------------------`);
    for (let i = 0; i < top3.length; i++) {
      const result = top3[i];
      const sizeMB = (result.size / (1024 * 1024)).toFixed(1);

      logger.info(`${i + 1}. "${result.title}"`);
      logger.info(`   Indexer: ${result.indexer}${result.indexerId ? ` (ID: ${result.indexerId})` : ''}`);
      logger.info(`   Format: ${result.ebookFormat || 'unknown'}`);
      logger.info(``);
      logger.info(`   Base Score: ${result.score.toFixed(1)}/100`);
      logger.info(`   - Title/Author Match: ${result.breakdown.matchScore.toFixed(1)}/60`);
      logger.info(`   - Format Match: ${result.breakdown.formatScore.toFixed(1)}/10`);
      logger.info(`   - Size Quality: ${result.breakdown.sizeScore.toFixed(1)}/15 (${sizeMB} MB)`);
      logger.info(`   - Seeder Count: ${result.breakdown.seederScore.toFixed(1)}/15 (${result.seeders !== undefined ? result.seeders + ' seeders' : 'N/A for Usenet'})`);
      logger.info(``);
      logger.info(`   Bonus Points: +${result.bonusPoints.toFixed(1)}`);
      if (result.bonusModifiers.length > 0) {
        for (const mod of result.bonusModifiers) {
          logger.info(`   - ${mod.reason}: +${mod.points.toFixed(1)}`);
        }
      }
      logger.info(``);
      logger.info(`   Final Score: ${result.finalScore.toFixed(1)}`);
      if (result.breakdown.notes.length > 0) {
        logger.info(`   Notes: ${result.breakdown.notes.join(', ')}`);
      }
      if (i < top3.length - 1) {
        logger.info(`--------------------------------------------------------------`);
      }
    }
    logger.info(`==============================================================`);
  }

  // Convert to unified result type
  return rankedResults.map((result: RankedEbookTorrent): EbookSearchResult => ({
    guid: result.guid,
    title: result.title,
    size: result.size,
    seeders: result.seeders,
    indexer: result.indexer,
    indexerId: result.indexerId,
    publishDate: result.publishDate,
    downloadUrl: result.downloadUrl,
    infoUrl: result.infoUrl,

    score: result.score,
    finalScore: result.finalScore,
    bonusPoints: result.bonusPoints,
    bonusModifiers: result.bonusModifiers,
    rank: result.rank,
    breakdown: result.breakdown,

    source: 'prowlarr',
    format: result.ebookFormat,
    protocol: result.protocol,
  }));
}
