import 'server-only';

import { createHash } from 'node:crypto';
import { desc, eq } from 'drizzle-orm';
import { seedManifestItems, seedManifests } from '@/db/schema';
import { db } from '@/lib/db';
import type { ImportFileReport, ImportReport } from '@/features/setup/server/csv/import-report';
import type { CsvPreviewFileInput } from './csv/types';
import { getActiveSetupRunId } from './setup-state.service';

function createId(prefix: string) {
  return `${prefix}_${crypto.randomUUID()}`;
}

function sha256(content: string | Uint8Array) {
  return `sha256:${createHash('sha256').update(content).digest('hex')}`;
}

function normalizeCsvBytes(bytes: Uint8Array) {
  return Buffer.from(bytes).toString('utf8').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
}

function buildManifestChecksum(input: {
  name: string;
  version: string;
  type: string;
  source: string;
  items: Array<{ itemKey: string; checksum: string }>;
}) {
  return sha256(
    JSON.stringify({
      name: input.name,
      version: input.version,
      type: input.type,
      source: input.source,
      items: input.items.toSorted((a, b) => a.itemKey.localeCompare(b.itemKey))
    })
  );
}

function toItemStatus(file: ImportFileReport): 'applied' | 'warning' | 'failed' | 'skipped' {
  if (file.status === 'FAIL') return 'failed';
  if (file.status === 'WARNING') return 'warning';
  if (file.imported === 0 && file.updated === 0 && file.skipped > 0) return 'skipped';
  return 'applied';
}

export async function createCsvImportSeedManifest(input: {
  files: CsvPreviewFileInput[];
  report: ImportReport;
  actorId: string;
  organizationId?: string | null;
}) {
  if (input.report.dryRun || input.report.status === 'FAIL') {
    return null;
  }

  const checksumByFile = new Map(
    input.files.map((file) => [file.fileName, sha256(normalizeCsvBytes(file.bytes))])
  );
  const reportFiles = input.report.files.filter((file) => !file.fileName.startsWith('__'));
  const itemChecksums = reportFiles.map((file) => ({
    itemKey: file.fileName,
    checksum: checksumByFile.get(file.fileName) ?? sha256(file.fileName)
  }));
  const manifestChecksum = buildManifestChecksum({
    name: 'crm-business-import',
    version: '1.0.0',
    type: 'business',
    source: 'csv',
    items: itemChecksums
  });
  const existingManifests = await db
    .select()
    .from(seedManifests)
    .where(eq(seedManifests.name, 'crm-business-import'))
    .orderBy(desc(seedManifests.createdAt));
  const hasSameChecksum = existingManifests.some(
    (manifest) => manifest.version === '1.0.0' && manifest.checksum === manifestChecksum
  );
  const hasChangedChecksum = existingManifests.some(
    (manifest) => manifest.version === '1.0.0' && manifest.checksum !== manifestChecksum
  );
  const status = hasSameChecksum ? 'skipped' : hasChangedChecksum ? 'warning' : 'applied';
  const timestamp = new Date();
  const setupRunId = await getActiveSetupRunId();
  const manifestId = createId('seedmf');

  await db.insert(seedManifests).values({
    id: manifestId,
    setupRunId,
    organizationId: input.organizationId ?? null,
    name: 'crm-business-import',
    version: '1.0.0',
    type: 'business',
    checksum: manifestChecksum,
    status,
    source: 'csv',
    appliedBy: input.actorId,
    appliedAt: timestamp,
    reportJson: {
      status: input.report.status,
      summary: input.report.summary,
      previewHash: input.report.previewHash,
      committedHash: input.report.committedHash,
      warnings: input.report.warnings,
      errors: input.report.errors
    },
    createdAt: timestamp,
    updatedAt: timestamp
  });

  await Promise.all(
    reportFiles.map((file) =>
      db.insert(seedManifestItems).values({
        id: createId('seedmi'),
        seedManifestId: manifestId,
        itemKey: file.fileName,
        sourceFile: file.fileName,
        checksum: checksumByFile.get(file.fileName) ?? sha256(file.fileName),
        status: status === 'skipped' ? 'skipped' : toItemStatus(file),
        importedCount: file.imported,
        updatedCount: file.updated,
        skippedCount: file.skipped,
        errorCount: file.errors.length,
        warningCount: file.warnings.length,
        reportJson: {
          template: file.template,
          status: file.status,
          imported: file.imported,
          updated: file.updated,
          skipped: file.skipped,
          errors: file.errors,
          warnings: file.warnings
        },
        createdAt: timestamp,
        updatedAt: timestamp
      })
    )
  );

  return manifestId;
}
