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

import { commitSetupCsvImport } from '@/features/setup/server/csv-commit.service';
import { createCsvImportSeedManifest } from '@/features/setup/server/seed-manifest.service';
import {
  BOOTSTRAP_OPTIONAL_TEMPLATE_FILES,
  BOOTSTRAP_REQUIRED_TEMPLATE_FILES
} from '@/features/setup/server/setup-bootstrap.service';
import { readConfiguredSetupImportSource } from '@/features/setup/server/setup-import-source.service';
import { AuthError, requireSystemRole } from '@/lib/auth/session';
import type { CsvPreviewFileInput } from '@/features/setup/server/csv/types';

function isFile(value: FormDataEntryValue): value is File {
  return typeof value === 'object' && 'arrayBuffer' in value && 'name' in value;
}

function parseBoolean(value: FormDataEntryValue | string | boolean | undefined | null): boolean {
  if (typeof value === 'boolean') return value;
  if (typeof value !== 'string') return false;
  return value.trim().toLowerCase() === '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 commitConfiguredDirectory(request: NextRequest, actorId: string) {
  const body = (await request.json().catch(() => ({}))) as {
    source?: string;
    previewHash?: string;
    dryRun?: boolean | string;
  };

  if (body.source !== 'configured-directory') {
    return NextResponse.json({ message: 'Unsupported setup import commit 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 }
    );
  }

  const report = await commitSetupCsvImport({
    files: source.files,
    previewHash: body.previewHash ?? '',
    dryRun: parseBoolean(body.dryRun),
    actorId
  });

  if (!report.dryRun && report.status !== 'FAIL') {
    await createCsvImportSeedManifest({ files: source.files, report, actorId });
  }

  return NextResponse.json({ ...report, importSource: source.report }, { status: report.status === 'FAIL' ? 400 : 200 });
}

async function commitUploadedFiles(request: NextRequest, actorId: string) {
  const formData = await request.formData();
  const previewHash = String(formData.get('previewHash') ?? '');
  const dryRun = parseBoolean(formData.get('dryRun'));
  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 report = await commitSetupCsvImport({
    files,
    previewHash,
    dryRun,
    actorId
  });

  if (!dryRun && report.status !== 'FAIL') {
    await createCsvImportSeedManifest({ files, report, actorId });
  }

  return NextResponse.json(report, { status: report.status === 'FAIL' ? 400 : 200 });
}

export async function POST(request: NextRequest) {
  try {
    const session = await requireSystemRole('super_admin');

    if (request.headers.get('content-type')?.includes('application/json')) {
      return await commitConfiguredDirectory(request, session.user.id);
    }

    return await commitUploadedFiles(request, session.user.id);
  } catch (error) {
    if (error instanceof AuthError) {
      return NextResponse.json({ message: error.message }, { status: error.status });
    }

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