/**
 * Component: Admin Manual Import API
 * Documentation: documentation/features/manual-import.md
 *
 * Triggers the organize_files pipeline for a manually-selected folder.
 * Creates or recycles a request, then queues the organize job.
 */

import { NextRequest, NextResponse } from 'next/server';
import { requireAuth, requireAdmin, AuthenticatedRequest } from '@/lib/middleware/auth';
import { prisma } from '@/lib/db';
import { getJobQueueService } from '@/lib/services/job-queue.service';
import { RMABLogger } from '@/lib/utils/logger';
import { AUDIO_EXTENSIONS } from '@/lib/constants/audio-formats';
import { getAudibleService } from '@/lib/integrations/audible.service';

const logger = RMABLogger.create('API.Admin.ManualImport');

/** Statuses that indicate the request is actively being worked on. */
const ACTIVE_STATUSES = ['searching', 'downloading', 'processing', 'awaiting_import'];

/** Statuses that can be recycled for a new manual import. */
const RECYCLABLE_STATUSES = ['failed', 'warn', 'cancelled', 'denied', 'pending', 'awaiting_search', 'awaiting_approval'];

/**
 * Check if a directory contains at least one audio file (immediate children only).
 */
async function hasAudioFiles(dirPath: string): Promise<{ found: boolean; count: number }> {
  const fs = await import('fs/promises');
  const pathModule = await import('path');

  let count = 0;
  try {
    const children = await fs.readdir(dirPath, { withFileTypes: true });
    for (const child of children) {
      if (child.isFile()) {
        const ext = pathModule.extname(child.name).toLowerCase();
        if ((AUDIO_EXTENSIONS as readonly string[]).includes(ext)) {
          count++;
        }
      }
    }
  } catch {
    /* directory not readable */
  }

  return { found: count > 0, count };
}

export async function POST(request: NextRequest) {
  return requireAuth(request, async (req: AuthenticatedRequest) => {
    return requireAdmin(req, async () => {
      try {
        const pathModule = await import('path');
        const fs = await import('fs/promises');

        const body = await request.json();
        const { folderPath, asin, cleanupSource, selectedFiles } = body;
        let { audiobookId } = body;

        // Validate selectedFiles if provided
        if (selectedFiles !== undefined) {
          if (!Array.isArray(selectedFiles) || selectedFiles.length === 0) {
            return NextResponse.json(
              { error: 'selectedFiles must be a non-empty array of file names' },
              { status: 400 }
            );
          }
          if (!selectedFiles.every((f: unknown) => typeof f === 'string')) {
            return NextResponse.json(
              { error: 'selectedFiles must contain only strings' },
              { status: 400 }
            );
          }
        }

        // Validate required fields
        if ((!audiobookId && !asin) || !folderPath) {
          return NextResponse.json(
            { error: 'folderPath and either audiobookId or asin are required' },
            { status: 400 }
          );
        }

        // Load allowed roots
        const BOOKDROP_PATH = '/bookdrop';
        const downloadDirConfig = await prisma.configuration.findUnique({
          where: { key: 'download_dir' },
        });
        const mediaDirConfig = await prisma.configuration.findUnique({
          where: { key: 'media_dir' },
        });

        const allowedRoots: string[] = [];
        if (downloadDirConfig?.value) {
          allowedRoots.push(pathModule.resolve(downloadDirConfig.value).replace(/\\/g, '/'));
        }
        if (mediaDirConfig?.value) {
          allowedRoots.push(pathModule.resolve(mediaDirConfig.value).replace(/\\/g, '/'));
        }
        try {
          const bookdropStat = await fs.stat(BOOKDROP_PATH);
          if (bookdropStat.isDirectory()) {
            allowedRoots.push(pathModule.resolve(BOOKDROP_PATH).replace(/\\/g, '/'));
          }
        } catch {
          /* not mounted */
        }

        // Normalize and validate path
        const normalizedPath = pathModule.resolve(folderPath).replace(/\\/g, '/');
        const isAllowed = allowedRoots.some(
          (root) => normalizedPath === root || normalizedPath.startsWith(root + '/')
        );

        if (!isAllowed) {
          return NextResponse.json(
            { error: 'Access denied: path outside allowed directories' },
            { status: 403 }
          );
        }

        // Verify folder exists and is a directory
        try {
          const stat = await fs.stat(normalizedPath);
          if (!stat.isDirectory()) {
            return NextResponse.json(
              { error: 'Path is not a directory' },
              { status: 400 }
            );
          }
        } catch {
          return NextResponse.json(
            { error: 'Directory not found' },
            { status: 404 }
          );
        }

        // Verify selected files exist and are audio files, or fall back to folder scan
        let audioFileCount: number;
        const validatedFiles: string[] = [];

        if (selectedFiles && selectedFiles.length > 0) {
          for (const fileName of selectedFiles as string[]) {
            // Prevent path traversal
            if (fileName.includes('/') || fileName.includes('\\') || fileName === '..' || fileName === '.') {
              return NextResponse.json(
                { error: `Invalid file name: ${fileName}` },
                { status: 400 }
              );
            }
            const ext = pathModule.extname(fileName).toLowerCase();
            if (!(AUDIO_EXTENSIONS as readonly string[]).includes(ext)) {
              return NextResponse.json(
                { error: `Not an audio file: ${fileName}` },
                { status: 400 }
              );
            }
            try {
              const fileStat = await fs.stat(pathModule.join(normalizedPath, fileName));
              if (!fileStat.isFile()) {
                return NextResponse.json(
                  { error: `Not a file: ${fileName}` },
                  { status: 400 }
                );
              }
              validatedFiles.push(fileName);
            } catch {
              return NextResponse.json(
                { error: `File not found: ${fileName}` },
                { status: 404 }
              );
            }
          }
          audioFileCount = validatedFiles.length;
        } else {
          const audioCheck = await hasAudioFiles(normalizedPath);
          if (!audioCheck.found) {
            return NextResponse.json(
              { error: 'No audio files found in the selected directory' },
              { status: 400 }
            );
          }
          audioFileCount = audioCheck.count;
        }

        // Resolve audiobook by ASIN if audiobookId not provided
        if (!audiobookId && asin) {
          const byAsin = await prisma.audiobook.findFirst({
            where: { audibleAsin: asin },
          });
          if (byAsin) {
            audiobookId = byAsin.id;
          } else {
            // Create audiobook record from Audible cache if available
            const cached = await prisma.audibleCache.findUnique({
              where: { asin },
            });
            if (cached) {
              const newBook = await prisma.audiobook.create({
                data: {
                  audibleAsin: asin,
                  title: cached.title,
                  author: cached.author,
                  coverArtUrl: cached.coverArtUrl,
                  narrator: cached.narrator,
                  status: 'pending',
                },
              });
              audiobookId = newBook.id;
              logger.info(`Created audiobook record from cache for ASIN ${asin}: ${newBook.id}`);
            } else {
              // Not in DB — fetch live from Audnexus and create a record
              try {
                const audibleService = getAudibleService();
                const liveData = await audibleService.getAudiobookDetails(asin);
                if (liveData) {
                  const newBook = await prisma.audiobook.create({
                    data: {
                      audibleAsin: asin,
                      title: liveData.title,
                      author: liveData.author,
                      coverArtUrl: liveData.coverArtUrl,
                      narrator: liveData.narrator,
                      series: liveData.series,
                      seriesPart: liveData.seriesPart,
                      seriesAsin: liveData.seriesAsin,
                      year: liveData.releaseDate
                        ? new Date(liveData.releaseDate).getFullYear() || undefined
                        : undefined,
                      status: 'pending',
                    },
                  });
                  audiobookId = newBook.id;
                  logger.info(`Created audiobook record from Audnexus for ASIN ${asin}: ${newBook.id}`);
                } else {
                  return NextResponse.json(
                    { error: 'Audiobook not found for the given ASIN' },
                    { status: 404 }
                  );
                }
              } catch (audnexusError) {
                logger.error(`Failed to fetch ASIN ${asin} from Audnexus: ${audnexusError instanceof Error ? audnexusError.message : String(audnexusError)}`);
                return NextResponse.json(
                  { error: 'Audiobook not found for the given ASIN' },
                  { status: 404 }
                );
              }
            }
          }
        }

        // Verify audiobook exists
        const audiobook = await prisma.audiobook.findUnique({
          where: { id: audiobookId },
        });

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

        // Enrich missing series/year data from Audnexus (mirrors request-creator.service.ts)
        if (audiobook.audibleAsin && (!audiobook.series || !audiobook.year)) {
          try {
            const audibleService = getAudibleService();
            const audnexusData = await audibleService.getAudiobookDetails(audiobook.audibleAsin);

            if (audnexusData) {
              const updates: Record<string, any> = {};

              if (!audiobook.series && audnexusData.series) {
                updates.series = audnexusData.series;
              }
              if (!audiobook.seriesPart && audnexusData.seriesPart) {
                updates.seriesPart = audnexusData.seriesPart;
              }
              if (!audiobook.seriesAsin && audnexusData.seriesAsin) {
                updates.seriesAsin = audnexusData.seriesAsin;
              }
              if (!audiobook.year && audnexusData.releaseDate) {
                const releaseYear = new Date(audnexusData.releaseDate).getFullYear();
                if (!isNaN(releaseYear)) {
                  updates.year = releaseYear;
                }
              }
              if (!audiobook.narrator && audnexusData.narrator) {
                updates.narrator = audnexusData.narrator;
              }

              if (Object.keys(updates).length > 0) {
                await prisma.audiobook.update({
                  where: { id: audiobook.id },
                  data: updates,
                });
                logger.info(`Enriched audiobook metadata from Audnexus for ASIN ${audiobook.audibleAsin}`, updates);
              }
            }
          } catch (error) {
            // Non-fatal: series enrichment failure should never block the import
            logger.warn(`Failed to enrich metadata from Audnexus for ASIN ${audiobook.audibleAsin}: ${error instanceof Error ? error.message : String(error)}`);
          }
        }

        // Check for existing requests
        const existingRequest = await prisma.request.findFirst({
          where: {
            audiobookId,
            type: 'audiobook',
            deletedAt: null,
          },
          orderBy: { createdAt: 'desc' },
        });

        let requestId: string;

        if (existingRequest) {
          // Check if already in an active state
          if (ACTIVE_STATUSES.includes(existingRequest.status)) {
            return NextResponse.json(
              { error: 'This audiobook is already being processed' },
              { status: 409 }
            );
          }

          // Recycle the existing request
          if (RECYCLABLE_STATUSES.includes(existingRequest.status) ||
              existingRequest.status === 'downloaded' ||
              existingRequest.status === 'available') {
            await prisma.request.update({
              where: { id: existingRequest.id },
              data: {
                status: 'processing',
                progress: 100,
                errorMessage: null,
                importAttempts: 0,
                updatedAt: new Date(),
              },
            });
            requestId = existingRequest.id;
            logger.info(`Recycled existing request ${requestId} for manual import`);
          } else {
            // Unknown status - create new
            const newRequest = await prisma.request.create({
              data: {
                userId: req.user!.id,
                audiobookId,
                type: 'audiobook',
                status: 'processing',
                progress: 100,
              },
            });
            requestId = newRequest.id;
            logger.info(`Created new request ${requestId} (existing had status: ${existingRequest.status})`);
          }
        } else {
          // No existing request - create one
          const newRequest = await prisma.request.create({
            data: {
              userId: req.user!.id,
              audiobookId,
              type: 'audiobook',
              status: 'processing',
              progress: 100,
            },
          });
          requestId = newRequest.id;
          logger.info(`Created new request ${requestId} for manual import`);
        }

        // Queue organize_files job
        const jobQueue = getJobQueueService();
        await jobQueue.addOrganizeJob(
          requestId,
          audiobookId,
          normalizedPath,
          undefined,
          cleanupSource === true,
          validatedFiles.length > 0 ? validatedFiles : undefined
        );

        logger.info(`Manual import queued: request=${requestId}, path=${normalizedPath}, audioFiles=${audioFileCount}`);

        return NextResponse.json({
          success: true,
          requestId,
          message: `Import started for ${audiobook.title}`,
        });
      } catch (error) {
        logger.error('Manual import failed', {
          error: error instanceof Error ? error.message : String(error),
        });
        return NextResponse.json(
          { error: error instanceof Error ? error.message : 'Manual import failed' },
          { status: 500 }
        );
      }
    });
  });
}
