/**
 * Component: Setup Wizard Complete API
 * Documentation: documentation/setup-wizard.md
 */

import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/db';
import bcrypt from 'bcrypt';
import { generateAccessToken, generateRefreshToken } from '@/lib/utils/jwt';
import { getEncryptionService } from '@/lib/services/encryption.service';
import { getPlexService } from '@/lib/integrations/plex.service';
import { getClientDisplayName } from '@/lib/interfaces/download-client.interface';
import { requireSetupIncomplete } from '@/lib/middleware/auth';
import { RMABLogger } from '@/lib/utils/logger';

const logger = RMABLogger.create('API.Setup.Complete');

export async function POST(request: NextRequest) {
  return requireSetupIncomplete(request, async (req) => {
  try {
    const {
      backendMode,
      audibleRegion,
      admin,
      plex,
      audiobookshelf,
      authMethod,
      oidc,
      registration,
      prowlarr,
      downloadClient,
      paths,
      bookdate,
    } = await req.json();

    // Validate backend mode
    if (!backendMode || !['plex', 'audiobookshelf'].includes(backendMode)) {
      return NextResponse.json(
        { success: false, error: 'Invalid or missing backend mode' },
        { status: 400 }
      );
    }

    // Validate required fields based on backend mode
    if (backendMode === 'plex') {
      if (
        !admin?.username ||
        !admin?.password ||
        !plex?.url ||
        !plex?.token ||
        !plex?.audiobook_library_id
      ) {
        return NextResponse.json(
          { success: false, error: 'Missing required Plex configuration fields' },
          { status: 400 }
        );
      }
    } else {
      // Audiobookshelf mode
      if (
        !audiobookshelf?.server_url ||
        !audiobookshelf?.api_token ||
        !audiobookshelf?.library_id
      ) {
        return NextResponse.json(
          { success: false, error: 'Missing required Audiobookshelf configuration fields' },
          { status: 400 }
        );
      }

      if (!authMethod || !['oidc', 'manual', 'both'].includes(authMethod)) {
        return NextResponse.json(
          { success: false, error: 'Invalid or missing authentication method' },
          { status: 400 }
        );
      }
    }

    // Validate common required fields
    if (
      !prowlarr?.url ||
      !prowlarr?.api_key ||
      !prowlarr?.indexers ||
      !Array.isArray(prowlarr.indexers) ||
      prowlarr.indexers.length === 0 ||
      !paths?.download_dir ||
      !paths?.media_dir
    ) {
      return NextResponse.json(
        { success: false, error: 'Missing required configuration fields' },
        { status: 400 }
      );
    }

    // Validate download client(s)
    if (!downloadClient) {
      return NextResponse.json(
        { success: false, error: 'Download client configuration is required' },
        { status: 400 }
      );
    }

    // Support both legacy single client and new multi-client array
    const clients = Array.isArray(downloadClient) ? downloadClient : [downloadClient];
    if (clients.length === 0) {
      return NextResponse.json(
        { success: false, error: 'At least one download client must be configured' },
        { status: 400 }
      );
    }

    // Validate each client has required fields
    // qBittorrent credentials are optional (supports IP whitelist auth)
    // SABnzbd always requires API key
    for (const client of clients) {
      if (!client.url) {
        return NextResponse.json(
          { success: false, error: 'Download client URL is required' },
          { status: 400 }
        );
      }
      if (client.type === 'sabnzbd' && !client.password) {
        return NextResponse.json(
          { success: false, error: 'SABnzbd API key is required' },
          { status: 400 }
        );
      }
    }

    // Create admin user (for Plex mode or ABS + Manual auth)
    let adminUser: any = null;
    let accessToken: string | null = null;
    let refreshToken: string | null = null;

    if (backendMode === 'plex' || (backendMode === 'audiobookshelf' && admin)) {
      if (!admin?.username || !admin?.password) {
        return NextResponse.json(
          { success: false, error: 'Admin credentials required' },
          { status: 400 }
        );
      }

      const normalizedAdminUsername = admin.username.trim().toLowerCase();
      const hashedPassword = await bcrypt.hash(admin.password, 10);
      const encryptionService = getEncryptionService();
      const encryptedPassword = encryptionService.encrypt(hashedPassword);

      adminUser = await prisma.user.create({
        data: {
          plexId: `local-${normalizedAdminUsername}`,
          plexUsername: normalizedAdminUsername,
          plexEmail: null,
          role: 'admin',
          isSetupAdmin: true, // Mark as setup admin - role cannot be changed
          avatarUrl: null,
          authToken: encryptedPassword, // Store encrypted hashed password
          authProvider: backendMode === 'plex' ? 'plex' : 'local',
          registrationStatus: 'approved',
          lastLoginAt: new Date(),
        },
      });

      // Generate JWT tokens for auto-login
      accessToken = generateAccessToken({
        sub: adminUser.id,
        plexId: adminUser.plexId,
        username: adminUser.plexUsername,
        role: adminUser.role,
      });

      refreshToken = generateRefreshToken(adminUser.id);
    }

    // Save configuration to database
    // Use upsert to handle both initial setup and updates
    const encryptionService = getEncryptionService();

    // Save backend mode
    await prisma.configuration.upsert({
      where: { key: 'system.backend_mode' },
      update: { value: backendMode },
      create: { key: 'system.backend_mode', value: backendMode },
    });

    // Save Audible region (default to 'us' if not provided)
    await prisma.configuration.upsert({
      where: { key: 'audible.region' },
      update: { value: audibleRegion || 'us', category: 'system' },
      create: { key: 'audible.region', value: audibleRegion || 'us', category: 'system' },
    });

    if (backendMode === 'plex') {
      // Plex configuration
      await prisma.configuration.upsert({
        where: { key: 'plex_url' },
        update: { value: plex.url },
        create: { key: 'plex_url', value: plex.url },
      });

      const encryptedPlexToken = encryptionService.encrypt(plex.token);
      await prisma.configuration.upsert({
        where: { key: 'plex_token' },
        update: { value: encryptedPlexToken, encrypted: true },
        create: { key: 'plex_token', value: encryptedPlexToken, encrypted: true },
      });

      await prisma.configuration.upsert({
        where: { key: 'plex_audiobook_library_id' },
        update: { value: plex.audiobook_library_id },
        create: { key: 'plex_audiobook_library_id', value: plex.audiobook_library_id },
      });

      // Get and save machine identifier (for server-specific access tokens)
      // Fetch from Plex if not provided by frontend
      let machineIdentifier = plex.machine_identifier;
      if (!machineIdentifier) {
        try {
          const plexService = getPlexService();
          const serverInfo = await plexService.testConnection(plex.url, plex.token);
          if (serverInfo.success && serverInfo.info?.machineIdentifier) {
            machineIdentifier = serverInfo.info.machineIdentifier;
            logger.debug('Fetched machineIdentifier', { machineIdentifier });
          } else {
            logger.warn('Could not fetch machineIdentifier');
          }
        } catch (error) {
          logger.error('Error fetching machineIdentifier', { error: error instanceof Error ? error.message : String(error) });
        }
      }

      if (machineIdentifier) {
        await prisma.configuration.upsert({
          where: { key: 'plex_machine_identifier' },
          update: { value: machineIdentifier },
          create: { key: 'plex_machine_identifier', value: machineIdentifier },
        });
      }

      // Save trigger_scan_after_import setting
      await prisma.configuration.upsert({
        where: { key: 'plex.trigger_scan_after_import' },
        update: { value: plex.trigger_scan_after_import === true ? 'true' : 'false' },
        create: { key: 'plex.trigger_scan_after_import', value: plex.trigger_scan_after_import === true ? 'true' : 'false' },
      });
    } else {
      // Audiobookshelf configuration
      await prisma.configuration.upsert({
        where: { key: 'audiobookshelf.server_url' },
        update: { value: audiobookshelf.server_url },
        create: { key: 'audiobookshelf.server_url', value: audiobookshelf.server_url },
      });

      const encryptedAbsToken = encryptionService.encrypt(audiobookshelf.api_token);
      await prisma.configuration.upsert({
        where: { key: 'audiobookshelf.api_token' },
        update: { value: encryptedAbsToken, encrypted: true },
        create: { key: 'audiobookshelf.api_token', value: encryptedAbsToken, encrypted: true },
      });

      await prisma.configuration.upsert({
        where: { key: 'audiobookshelf.library_id' },
        update: { value: audiobookshelf.library_id },
        create: { key: 'audiobookshelf.library_id', value: audiobookshelf.library_id },
      });

      // Save trigger_scan_after_import setting
      await prisma.configuration.upsert({
        where: { key: 'audiobookshelf.trigger_scan_after_import' },
        update: { value: audiobookshelf.trigger_scan_after_import === true ? 'true' : 'false' },
        create: { key: 'audiobookshelf.trigger_scan_after_import', value: audiobookshelf.trigger_scan_after_import === true ? 'true' : 'false' },
      });

      // OIDC configuration (if enabled)
      if (authMethod === 'oidc' || authMethod === 'both') {
        await prisma.configuration.upsert({
          where: { key: 'oidc.enabled' },
          update: { value: 'true' },
          create: { key: 'oidc.enabled', value: 'true' },
        });

        await prisma.configuration.upsert({
          where: { key: 'oidc.provider_name' },
          update: { value: oidc.provider_name },
          create: { key: 'oidc.provider_name', value: oidc.provider_name },
        });

        await prisma.configuration.upsert({
          where: { key: 'oidc.issuer_url' },
          update: { value: oidc.issuer_url },
          create: { key: 'oidc.issuer_url', value: oidc.issuer_url },
        });

        await prisma.configuration.upsert({
          where: { key: 'oidc.client_id' },
          update: { value: oidc.client_id },
          create: { key: 'oidc.client_id', value: oidc.client_id },
        });

        const encryptedClientSecret = encryptionService.encrypt(oidc.client_secret);
        await prisma.configuration.upsert({
          where: { key: 'oidc.client_secret' },
          update: { value: encryptedClientSecret, encrypted: true },
          create: { key: 'oidc.client_secret', value: encryptedClientSecret, encrypted: true },
        });

        // Access control configuration
        await prisma.configuration.upsert({
          where: { key: 'oidc.access_control_method' },
          update: { value: oidc.access_control_method || 'open' },
          create: { key: 'oidc.access_control_method', value: oidc.access_control_method || 'open' },
        });

        await prisma.configuration.upsert({
          where: { key: 'oidc.access_group_claim' },
          update: { value: oidc.access_group_claim || 'groups' },
          create: { key: 'oidc.access_group_claim', value: oidc.access_group_claim || 'groups' },
        });

        await prisma.configuration.upsert({
          where: { key: 'oidc.access_group_value' },
          update: { value: oidc.access_group_value || '' },
          create: { key: 'oidc.access_group_value', value: oidc.access_group_value || '' },
        });

        await prisma.configuration.upsert({
          where: { key: 'oidc.allowed_emails' },
          update: { value: oidc.allowed_emails || '[]' },
          create: { key: 'oidc.allowed_emails', value: oidc.allowed_emails || '[]' },
        });

        await prisma.configuration.upsert({
          where: { key: 'oidc.allowed_usernames' },
          update: { value: oidc.allowed_usernames || '[]' },
          create: { key: 'oidc.allowed_usernames', value: oidc.allowed_usernames || '[]' },
        });

        // Admin role mapping configuration
        await prisma.configuration.upsert({
          where: { key: 'oidc.admin_claim_enabled' },
          update: { value: oidc.admin_claim_enabled || 'false' },
          create: { key: 'oidc.admin_claim_enabled', value: oidc.admin_claim_enabled || 'false' },
        });

        await prisma.configuration.upsert({
          where: { key: 'oidc.admin_claim_name' },
          update: { value: oidc.admin_claim_name || 'groups' },
          create: { key: 'oidc.admin_claim_name', value: oidc.admin_claim_name || 'groups' },
        });

        await prisma.configuration.upsert({
          where: { key: 'oidc.admin_claim_value' },
          update: { value: oidc.admin_claim_value || '' },
          create: { key: 'oidc.admin_claim_value', value: oidc.admin_claim_value || '' },
        });
      }

      // Manual registration configuration (if enabled)
      if (authMethod === 'manual' || authMethod === 'both') {
        await prisma.configuration.upsert({
          where: { key: 'auth.registration_enabled' },
          update: { value: 'true' },
          create: { key: 'auth.registration_enabled', value: 'true' },
        });

        await prisma.configuration.upsert({
          where: { key: 'auth.require_admin_approval' },
          update: { value: registration.require_admin_approval ? 'true' : 'false' },
          create: {
            key: 'auth.require_admin_approval',
            value: registration.require_admin_approval ? 'true' : 'false',
          },
        });
      }
    }

    // Prowlarr configuration
    await prisma.configuration.upsert({
      where: { key: 'prowlarr_url' },
      update: { value: prowlarr.url },
      create: { key: 'prowlarr_url', value: prowlarr.url },
    });

    const encryptedProwlarrApiKey = encryptionService.encrypt(prowlarr.api_key);
    await prisma.configuration.upsert({
      where: { key: 'prowlarr_api_key' },
      update: { value: encryptedProwlarrApiKey, encrypted: true },
      create: { key: 'prowlarr_api_key', value: encryptedProwlarrApiKey, encrypted: true },
    });

    await prisma.configuration.upsert({
      where: { key: 'prowlarr_indexers' },
      update: { value: JSON.stringify(prowlarr.indexers) },
      create: { key: 'prowlarr_indexers', value: JSON.stringify(prowlarr.indexers) },
    });

    // Download clients configuration (multi-client support)
    // Accept either legacy single client or new clients array
    let downloadClientsArray: any[];

    if (Array.isArray(downloadClient)) {
      // New format: array of clients
      downloadClientsArray = downloadClient;
    } else if (downloadClient && typeof downloadClient === 'object') {
      // Legacy format: convert single client to array
      downloadClientsArray = [{
        id: `temp-${Date.now()}`,
        type: downloadClient.type,
        name: getClientDisplayName(downloadClient.type),
        enabled: true,
        url: downloadClient.url,
        username: downloadClient.username,
        password: downloadClient.password,
        disableSSLVerify: downloadClient.disableSSLVerify || false,
        remotePathMappingEnabled: downloadClient.remotePathMappingEnabled || false,
        remotePath: downloadClient.remotePath,
        localPath: downloadClient.localPath,
        category: 'readmeabook',
      }];
    } else {
      throw new Error('Invalid download client configuration');
    }

    // Encrypt passwords in download clients
    const encryptedClients = downloadClientsArray.map(client => ({
      ...client,
      password: client.password ? encryptionService.encrypt(client.password) : '',
    }));

    await prisma.configuration.upsert({
      where: { key: 'download_clients' },
      update: { value: JSON.stringify(encryptedClients) },
      create: { key: 'download_clients', value: JSON.stringify(encryptedClients) },
    });

    // Path configuration
    await prisma.configuration.upsert({
      where: { key: 'download_dir' },
      update: { value: paths.download_dir },
      create: { key: 'download_dir', value: paths.download_dir },
    });

    await prisma.configuration.upsert({
      where: { key: 'media_dir' },
      update: { value: paths.media_dir },
      create: { key: 'media_dir', value: paths.media_dir },
    });

    // Metadata tagging configuration
    await prisma.configuration.upsert({
      where: { key: 'metadata_tagging_enabled' },
      update: { value: String(paths.metadata_tagging_enabled ?? true) },
      create: {
        key: 'metadata_tagging_enabled',
        value: String(paths.metadata_tagging_enabled ?? true),
        category: 'automation',
        description: 'Automatically tag audio files with correct metadata during file organization'
      },
    });

    // Chapter merging configuration
    await prisma.configuration.upsert({
      where: { key: 'chapter_merging_enabled' },
      update: { value: String(paths.chapter_merging_enabled ?? false) },
      create: {
        key: 'chapter_merging_enabled',
        value: String(paths.chapter_merging_enabled ?? false),
        category: 'automation',
        description: 'Automatically merge multi-file chapter downloads into single M4B with chapter markers'
      },
    });

    // Plex format coercion configuration (issue #166: silently rename .mp4/.m4a to .m4b
    // post-organize so Plex's audiobook library recognizes them without transcoding)
    await prisma.configuration.upsert({
      where: { key: 'plex_format_coercion_enabled' },
      update: { value: String(paths.plex_format_coercion_enabled ?? true) },
      create: {
        key: 'plex_format_coercion_enabled',
        value: String(paths.plex_format_coercion_enabled ?? true),
        category: 'automation',
        description: 'Rename audio files to Plex-compatible extensions (.mp4/.m4a -> .m4b) after organization to avoid silent library-scan failures'
      },
    });

    // BookDate configuration (optional, global for all users)
    // Note: libraryScope and customPrompt are now per-user settings, not required here
    if (bookdate && bookdate.provider && bookdate.apiKey && bookdate.model) {
      logger.info('Saving global BookDate configuration');

      const encryptionService = getEncryptionService();
      const encryptedApiKey = encryptionService.encrypt(bookdate.apiKey);

      // Check if global config already exists
      const existingConfig = await prisma.bookDateConfig.findFirst();

      if (existingConfig) {
        // Update existing global config
        await prisma.bookDateConfig.update({
          where: { id: existingConfig.id },
          data: {
            provider: bookdate.provider,
            apiKey: encryptedApiKey,
            model: bookdate.model,
            isVerified: true,
            isEnabled: true,
          },
        });
      } else {
        // Create new global config
        await prisma.bookDateConfig.create({
          data: {
            provider: bookdate.provider,
            apiKey: encryptedApiKey,
            model: bookdate.model,
            isVerified: true,
            isEnabled: true,
          },
        });
      }

      logger.debug('Global BookDate configuration saved');
    } else {
      logger.debug('BookDate configuration skipped (missing provider, apiKey, or model)');
    }

    // Mark setup as complete
    await prisma.configuration.upsert({
      where: { key: 'setup_completed' },
      update: { value: 'true' },
      create: { key: 'setup_completed', value: 'true' },
    });

    // Enable auto jobs (Plex Library Scan and Audible Data Refresh)
    await prisma.scheduledJob.updateMany({
      where: {
        type: {
          in: ['plex_library_scan', 'audible_refresh'],
        },
      },
      data: {
        enabled: true,
      },
    });

    logger.debug('Auto jobs enabled');

    logger.info('Configuration saved successfully');

    // Return response with tokens if admin user was created
    if (adminUser && accessToken && refreshToken) {
      return NextResponse.json({
        success: true,
        message: 'Setup completed successfully',
        accessToken,
        refreshToken,
        user: {
          id: adminUser.id,
          plexId: adminUser.plexId,
          username: adminUser.plexUsername,
          email: adminUser.plexEmail,
          role: adminUser.role,
          avatarUrl: adminUser.avatarUrl,
        },
      });
    } else {
      // OIDC-only mode - no admin user created yet
      return NextResponse.json({
        success: true,
        message: 'Setup completed successfully. First OIDC login will become admin.',
      });
    }
  } catch (error) {
    logger.error('Failed to save configuration', { error: error instanceof Error ? error.message : String(error) });
    return NextResponse.json(
      {
        success: false,
        error: error instanceof Error ? error.message : 'Failed to save configuration',
      },
      { status: 500 }
    );
  }
  });
}
