/**
 * gCTS / AFF format writer.
 *
 * gCTS (git-enabled CTS) and AFF (SAP/abap-file-formats) share the same
 * on-disk layout: JSON metadata, per-object subdirectory named after the
 * lowercased object name, `<name>.<type>.<ext>` naming.
 *
 * For a class this produces:
 *
 *   <outDir>/
 *     package.devc.json
 *     CLAS/<name>/<name>.clas.json
 *     CLAS/<name>/<name>.clas.abap
 *     CLAS/<name>/<name>.clas.testclasses.abap   (optional)
 *     CLAS/<name>/<name>.clas.locals_def.abap    (optional)
 *     CLAS/<name>/<name>.clas.locals_imp.abap    (optional)
 *
 * The `CLAS/` object-type subdirectory mirrors the layout used by gCTS
 * repositories (objects grouped under their type code). See
 * `packages/adt-plugin-gcts/` for filename conventions.
 */

import { mkdir, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import type { ClassArtifact, InterfaceArtifact, WriteResult } from './types';
import { assertValidClassName } from './validate';

const PACKAGE_DEVC_JSON =
  JSON.stringify(
    {
      header: {
        formatVersion: '1.0',
        description: 'Generated by @abapify/openai-codegen',
      },
      package: {
        softwareComponent: 'LOCAL',
      },
    },
    null,
    2,
  ) + '\n';

function buildClasJson(
  className: string,
  description: string,
  language: string,
): string {
  return (
    JSON.stringify(
      {
        header: {
          formatVersion: '1.0',
          description,
          originalLanguage: language,
          // `5` = ABAP for Cloud Development (Steampunk). Required for
          // deployments that target ABAP Cloud — without it gCTS imports
          // default to Standard ABAP and the object fails to activate on
          // BTP with S_ABPLNGVS errors.
          abapLanguageVersion: '5',
        },
        class: {
          name: className,
          category: '00',
          visibility: 'public',
          final: true,
          abstract: false,
          fixedPointArithmetic: true,
          unicodeChecksActive: true,
        },
      },
      null,
      2,
    ) + '\n'
  );
}

export async function writeGctsLayout(
  artifact: ClassArtifact,
  outDir: string,
): Promise<WriteResult> {
  assertValidClassName(artifact.className);

  const language = artifact.language ?? 'E';
  const description = artifact.description ?? artifact.className;
  const base = artifact.className.toLowerCase();
  const objectDir = join(outDir, 'CLAS', base);

  await mkdir(objectDir, { recursive: true });

  const written: string[] = [];

  const pkgPath = join(outDir, 'package.devc.json');
  await writeFile(pkgPath, PACKAGE_DEVC_JSON, 'utf-8');
  written.push('package.devc.json');

  const metaPath = join(objectDir, `${base}.clas.json`);
  await writeFile(
    metaPath,
    buildClasJson(artifact.className, description, language),
    'utf-8',
  );
  written.push(`CLAS/${base}/${base}.clas.json`);

  const mainPath = join(objectDir, `${base}.clas.abap`);
  await writeFile(mainPath, artifact.mainSource, 'utf-8');
  written.push(`CLAS/${base}/${base}.clas.abap`);

  if (artifact.testSource !== undefined) {
    const p = join(objectDir, `${base}.clas.testclasses.abap`);
    await writeFile(p, artifact.testSource, 'utf-8');
    written.push(`CLAS/${base}/${base}.clas.testclasses.abap`);
  }

  if (artifact.localsDefSource !== undefined) {
    const p = join(objectDir, `${base}.clas.locals_def.abap`);
    await writeFile(p, artifact.localsDefSource, 'utf-8');
    written.push(`CLAS/${base}/${base}.clas.locals_def.abap`);
  }

  if (artifact.localsImpSource !== undefined) {
    const p = join(objectDir, `${base}.clas.locals_imp.abap`);
    await writeFile(p, artifact.localsImpSource, 'utf-8');
    written.push(`CLAS/${base}/${base}.clas.locals_imp.abap`);
  }

  written.sort((a, b) => a.localeCompare(b));
  return { files: written };
}

function buildIntfJson(
  name: string,
  description: string,
  language: string,
): string {
  return (
    JSON.stringify(
      {
        header: {
          formatVersion: '1.0',
          description,
          originalLanguage: language,
          // Mirrors the class-envelope rule: every gCTS metadata file
          // carries `abapLanguageVersion: '5'` so BTP Steampunk imports
          // don't default back to Standard ABAP.
          abapLanguageVersion: '5',
        },
        interface: {
          name,
          visibility: 'public',
        },
      },
      null,
      2,
    ) + '\n'
  );
}

export async function writeGctsInterface(
  artifact: InterfaceArtifact,
  outDir: string,
): Promise<WriteResult> {
  assertValidClassName(artifact.name);

  const language = artifact.language ?? 'E';
  const description = artifact.description ?? artifact.name;
  const base = artifact.name.toLowerCase();
  const objectDir = join(outDir, 'INTF', base);

  await mkdir(objectDir, { recursive: true });

  const written: string[] = [];

  const pkgPath = join(outDir, 'package.devc.json');
  await writeFile(pkgPath, PACKAGE_DEVC_JSON, 'utf-8');
  written.push('package.devc.json');

  const metaPath = join(objectDir, `${base}.intf.json`);
  await writeFile(
    metaPath,
    buildIntfJson(artifact.name, description, language),
    'utf-8',
  );
  written.push(`INTF/${base}/${base}.intf.json`);

  const mainPath = join(objectDir, `${base}.intf.abap`);
  await writeFile(mainPath, artifact.source, 'utf-8');
  written.push(`INTF/${base}/${base}.intf.abap`);

  written.sort((a, b) => a.localeCompare(b));
  return { files: written };
}
