/**
 * Files merged into ZIP / GitHub export beyond conversionResult.engine files.
 * Reads analysisResults.translation.artifacts (same shape as MigrationFlow saves).
 */
import {
  applyMarker,
  buildManifest,
  hasMarker,
  warnMarkerStripped,
  type MarkerMetadata,
} from '@/lib/ai-marker';

type MarkerProjectLike = {
  id?: string;
  sourceLanguage?: string | null;
  targetLanguage?: string | null;
  config?: Record<string, unknown> | null;
};

export type BundleFileEntry = { path: string; content: string };

/**
 * The engine writes under `os.tmpdir()/scriba-out-<conversionId>/…` (see scriba-engine routes).
 * Stored `outputPath` values are absolute; ZIP/Git must use paths relative to the project root.
 */
export function normalizeEngineOutputZipPath(outputPath: string): string {
  const raw = outputPath.replace(/\\/g, '/').trim();
  if (!raw) return 'migrated/unknown.txt';

  // Helper: strip leading slashes and return if non-empty
  const rel = (s: string) => s.replace(/^\/+/u, '');

  // 1. Engine standard temp dir: scriba-out-<id>/...
  const scribaOut = raw.match(/scriba-out-[^/]+\/(.+)$/i);
  if (scribaOut?.[1]) {
    const r = rel(scribaOut[1]);
    return r.length > 0 ? r : 'migrated/unknown.txt';
  }

  // 2. scriba-output/ prefix
  const scribaOutput = raw.match(/(?:^|\/)scriba-output\/(.+)$/i);
  if (scribaOutput?.[1]) {
    const r = rel(scribaOutput[1]);
    return r.length > 0 ? r : 'migrated/unknown.txt';
  }

  // 3. macOS temp: /[private/]var/folders/<2-char>/<hash>/T/<any-run-dir>/...
  const macTemp = raw.match(/\/(?:private\/)?var\/folders\/[^/]+\/[^/]+\/T\/[^/]+\/(.+)$/i);
  if (macTemp?.[1]) {
    const r = rel(macTemp[1]);
    return r.length > 0 ? r : 'migrated/unknown.txt';
  }

  // 4. Linux /tmp/<any-run-dir>/... or /private/tmp/<any-run-dir>/...
  const linuxTmp = raw.match(/\/(?:private\/)?tmp\/[^/]+\/(.+)$/i);
  if (linuxTmp?.[1]) {
    const r = rel(linuxTmp[1]);
    return r.length > 0 ? r : 'migrated/unknown.txt';
  }

  // 5. Relative path — use as-is
  const noLeading = raw.replace(/^\/+/u, '');
  if (!noLeading.includes(':') && !noLeading.startsWith('..')) {
    return noLeading;
  }

  // 6. Last resort: find the first known project-structure prefix within the path
  const lower = noLeading.toLowerCase();
  for (const prefix of ['src/', 'lib/', 'migrated/', 'tests/', 'docs/', '.github/']) {
    const idx = lower.indexOf(prefix);
    if (idx >= 0) return noLeading.slice(idx);
  }

  // 7. Absolute fallback: use only the leaf filename
  const parts = noLeading.split('/').filter(Boolean);
  const leaf = parts.pop();
  return leaf ? `migrated/${leaf}` : 'migrated/unknown.txt';
}

export function readToolingFilesFromAnalysis(config: Record<string, unknown>): Array<{ path: string; content: string }> {
  return readArtifactArrayByKey(config, 'toolingFiles');
}

/** Docs + unit + integration artifact files when enabled on the project wizard. */
export function readDocsAndTestArtifactsFromAnalysis(config: Record<string, unknown>): Array<{ path: string; content: string }> {
  const addDocs = config.addDocs !== false;
  const addTests = config.addTests !== false;
  const keys: string[] = [];
  if (addDocs) keys.push('docFiles');
  if (addTests) {
    keys.push('unitTestFiles');
    keys.push('integrationTestFiles');
  }
  const out: Array<{ path: string; content: string }> = [];
  for (const key of keys) {
    out.push(...readArtifactArrayByKey(config, key));
  }
  return out;
}

function readArtifactArrayByKey(config: Record<string, unknown>, key: string): Array<{ path: string; content: string }> {
  const ar = config.analysisResults;
  if (!ar || typeof ar !== 'object') return [];
  const tr = (ar as Record<string, unknown>).translation;
  if (!tr || typeof tr !== 'object') return [];
  const art = (tr as Record<string, unknown>).artifacts;
  if (!art || typeof art !== 'object') return [];
  const arr = (art as Record<string, unknown>)[key];
  if (!Array.isArray(arr)) return [];
  const out: Array<{ path: string; content: string }> = [];
  for (const row of arr) {
    if (!row || typeof row !== 'object') continue;
    const r = row as Record<string, unknown>;
    const path = typeof r.path === 'string' ? r.path : '';
    const content = typeof r.code === 'string' ? r.code : '';
    if (!path || !content) continue;
    out.push({ path: path.startsWith('/') ? path.slice(1) : path, content });
  }
  return out;
}

function readConversionId(config: Record<string, unknown>, fallback: string): string {
  const conv = config.conversionResult;
  if (!conv || typeof conv !== 'object') return fallback;
  const metadata = (conv as Record<string, unknown>).metadata;
  if (!metadata || typeof metadata !== 'object') return fallback;
  const id = (metadata as Record<string, unknown>).conversionId;
  return typeof id === 'string' && id.trim().length > 0 ? id : fallback;
}

export function buildMarkerMetadata(project: MarkerProjectLike): MarkerMetadata {
  const config = (project.config ?? {}) as Record<string, unknown>;
  const fallbackId = project.id || `conv-${Date.now()}`;
  const completedAt = config.completedAt;
  return {
    conversionId: readConversionId(config, fallbackId),
    sourceLanguage: project.sourceLanguage ?? 'unknown',
    targetLanguage: project.targetLanguage ?? 'unknown',
    timestamp:
      typeof completedAt === 'string' && completedAt.trim().length > 0
        ? completedAt
        : new Date().toISOString(),
    platformVersion: process.env.npm_package_version ?? '1.0.0',
  };
}

export function applyMarkersToFiles(
  files: BundleFileEntry[],
  meta: MarkerMetadata
): { files: BundleFileEntry[]; manifestContent: string } {
  const out: BundleFileEntry[] = [];
  const markedPaths: string[] = [];

  for (const file of files) {
    const normalizedPath = file.path.startsWith('/') ? file.path.slice(1) : file.path;
    if (
      /@scriba-(marker-version|source-language|target-language)/.test(file.content ?? '') &&
      !hasMarker(file.content ?? '')
    ) {
      warnMarkerStripped(normalizedPath, meta.conversionId);
    }
    const result = applyMarker(normalizedPath, file.content ?? '', meta);
    out.push({ path: normalizedPath, content: result.marked });
    markedPaths.push(normalizedPath);

    if (result.usedSidecar && result.sidecarPath && result.sidecarContent) {
      const sidecarPath = result.sidecarPath.startsWith('/')
        ? result.sidecarPath.slice(1)
        : result.sidecarPath;
      out.push({ path: sidecarPath, content: result.sidecarContent });
      markedPaths.push(sidecarPath);
    }
  }

  return { files: out, manifestContent: buildManifest(meta, markedPaths) };
}

export function applyMarkersToProjectPayloadConfig(
  config: Record<string, unknown>,
  meta: MarkerMetadata
): Record<string, unknown> {
  const next = JSON.parse(JSON.stringify(config || {})) as Record<string, unknown>;

  const conversionResult = next.conversionResult;
  if (conversionResult && typeof conversionResult === 'object') {
    const convObj = conversionResult as Record<string, unknown>;
    const files = convObj.files;
    if (Array.isArray(files)) {
      convObj.files = files.map((f) => {
        if (!f || typeof f !== 'object') return f;
        const row = { ...(f as Record<string, unknown>) };
        const outputPath = typeof row.outputPath === 'string' ? row.outputPath : '';
        const content = typeof row.content === 'string' ? row.content : '';
        const result = applyMarker(normalizeEngineOutputZipPath(outputPath), content, meta);
        row.content = result.marked;
        return row;
      });
    }
  }

  const analysis = next.analysisResults;
  if (analysis && typeof analysis === 'object') {
    const translation = (analysis as Record<string, unknown>).translation;
    if (translation && typeof translation === 'object') {
      const trObj = translation as Record<string, unknown>;
      const trFiles = trObj.files;
      if (Array.isArray(trFiles)) {
        trObj.files = trFiles.map((f) => {
          if (!f || typeof f !== 'object') return f;
          const row = { ...(f as Record<string, unknown>) };
          const targetFile = typeof row.targetFile === 'string' ? row.targetFile : 'target.txt';
          const targetCode = typeof row.targetCode === 'string' ? row.targetCode : '';
          const result = applyMarker(targetFile, targetCode, meta);
          row.targetCode = result.marked;
          return row;
        });
      } else if (typeof trObj.targetCode === 'string') {
        const targetFile = typeof trObj.targetFile === 'string' ? trObj.targetFile : 'target.txt';
        const result = applyMarker(targetFile, trObj.targetCode, meta);
        trObj.targetCode = result.marked;
      }
    }
  }

  return next;
}
