import { NextRequest, NextResponse } from 'next/server';

import { previewSetupCsvImport } from '@/features/setup/server/csv-preview.service';
import {
  assertBootstrapSetupAvailable,
  assertBootstrapStepFiles,
  assertBootstrapToken,
  BOOTSTRAP_ACTOR_ID,
  BOOTSTRAP_OPTIONAL_TEMPLATE_FILES,
  BOOTSTRAP_REQUIRED_STEPS,
  BOOTSTRAP_REQUIRED_TEMPLATE_FILES,
  BootstrapSetupError,
  sanitizeCsvPreviewFileForPersistence,
  sanitizeCsvPreviewForResponse,
  setupStepStatusFromPreview,
  setupStepStatusFromReportStatus,
  toSerializableRecord
} from '@/features/setup/server/setup-bootstrap.service';
import { readConfiguredSetupImportSource } from '@/features/setup/server/setup-import-source.service';
import { saveSetupStep, startSetupRun } from '@/features/setup/server/setup-state.service';
import type { CsvPreviewFileInput, CsvPreviewResult } from '@/features/setup/server/csv/types';

function isFile(value: FormDataEntryValue): value is File {
  return (
    typeof value === 'object' &&
    'arrayBuffer' in value &&
    'name' in value &&
    typeof value.name === 'string' &&
    value.name.trim().length > 0
  );
}

async function saveConfiguredPreviewSteps(preview: CsvPreviewResult) {
  await Promise.all(
    BOOTSTRAP_REQUIRED_STEPS.map((step) => {
      const file = preview.files.find(
        (candidate) => candidate.fileName.toLowerCase() === step.fileName.toLowerCase()
      );

      if (!file) return Promise.resolve();

      return saveSetupStep({
        stepKey: step.stepKey,
        status: setupStepStatusFromReportStatus(file.status),
        inputHash: preview.previewHash,
        output: toSerializableRecord(sanitizeCsvPreviewFileForPersistence(preview, step.fileName)),
        errors: file.errors,
        warnings: file.warnings,
        skipStaleInvalidation: true
      });
    })
  );
}

async function readMultipartFiles(formData: FormData): Promise<CsvPreviewFileInput[]> {
  const uploadedFiles = Array.from(formData.values()).filter(isFile);

  return Promise.all(
    uploadedFiles.map(async (file) => ({
      fileName: file.name,
      bytes: new Uint8Array(await file.arrayBuffer())
    }))
  );
}

async function previewConfiguredDirectory(request: NextRequest) {
  const body = (await request.json().catch(() => ({}))) as {
    bootstrapToken?: string;
    source?: string;
  };

  assertBootstrapToken(body.bootstrapToken ?? request.headers.get('x-bootstrap-setup-token') ?? '');

  if (body.source !== 'configured-directory') {
    return NextResponse.json({ message: 'Unsupported bootstrap preview source.' }, { status: 400 });
  }

  const source = await readConfiguredSetupImportSource({
    requiredFiles: BOOTSTRAP_REQUIRED_TEMPLATE_FILES,
    optionalFiles: BOOTSTRAP_OPTIONAL_TEMPLATE_FILES
  });

  if (source.report.errors.length > 0) {
    return NextResponse.json(
      { message: 'Configured setup import folder is not ready.', importSource: source.report },
      { status: 400 }
    );
  }

  await startSetupRun({ actorId: BOOTSTRAP_ACTOR_ID, mode: 'production', targetOrganizationId: null });
  const preview = await previewSetupCsvImport(source.files);
  await saveConfiguredPreviewSteps(preview);

  return NextResponse.json({ ...sanitizeCsvPreviewForResponse(preview), importSource: source.report });
}

async function previewUploadedStep(request: NextRequest) {
  const formData = await request.formData();
  assertBootstrapToken(String(formData.get('bootstrapToken') ?? request.headers.get('x-bootstrap-setup-token') ?? ''));

  const stepKey = String(formData.get('stepKey') ?? '');
  const files = await readMultipartFiles(formData);

  if (files.length === 0) {
    return NextResponse.json(
      { message: 'Upload at least one setup CSV file using multipart/form-data.' },
      { status: 400 }
    );
  }

  const step = assertBootstrapStepFiles(stepKey, files);
  await startSetupRun({ actorId: BOOTSTRAP_ACTOR_ID, mode: 'production', targetOrganizationId: null });

  const preview = await previewSetupCsvImport(files);
  await saveSetupStep({
    stepKey: step.stepKey,
    status: setupStepStatusFromPreview(preview),
    inputHash: preview.previewHash,
    output: toSerializableRecord(sanitizeCsvPreviewFileForPersistence(preview, step.fileName)),
    errors: preview.files.flatMap((file) => file.errors),
    warnings: preview.files.flatMap((file) => file.warnings)
  });

  return NextResponse.json(sanitizeCsvPreviewForResponse(preview));
}

export async function POST(request: NextRequest) {
  try {
    await assertBootstrapSetupAvailable();

    if (request.headers.get('content-type')?.includes('application/json')) {
      return await previewConfiguredDirectory(request);
    }

    return await previewUploadedStep(request);
  } catch (error) {
    if (error instanceof BootstrapSetupError) {
      return NextResponse.json({ message: error.message, details: error.details }, { status: error.status });
    }

    if (error instanceof TypeError && /multipart\/form-data|application\/x-www-form-urlencoded/i.test(error.message)) {
      return NextResponse.json({ message: 'Upload setup CSV files using multipart/form-data.' }, { status: 400 });
    }

    return NextResponse.json({ message: 'Unable preview bootstrap setup CSV import' }, { status: 500 });
  }
}
