/**
 * abapGit format writer.
 *
 * Produces the canonical layout consumed by the abapGit import flow:
 *
 *   <outDir>/
 *     package.devc.xml
 *     src/<name>.clas.abap
 *     src/<name>.clas.xml
 *     src/<name>.clas.testclasses.abap   (optional)
 *     src/<name>.clas.locals_def.abap    (optional)
 *
 * File names are lowercase (abapGit convention). The `.clas.xml` envelope is
 * parameterised on CLSNAME / DESCRIPT / LANGU only — STATE/CLSCCINCL/FIXPT/
 * UNICODE are fixed to the defaults emitted by the SAP serializer for a
 * freshly-generated global class (mirrors samples/petstore-abapgit/).
 */

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_XML = `<?xml version="1.0" encoding="utf-8"?>
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
  <asx:values>
    <DEVC>
      <CTEXT>Generated by @abapify/openai-codegen</CTEXT>
    </DEVC>
  </asx:values>
</asx:abap>
`;

function buildClasXml(
  className: string,
  description: string,
  language: string,
): string {
  return `<?xml version="1.0" encoding="utf-8"?>
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
  <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
    <asx:values>
      <VSEOCLASS>
        <CLSNAME>${className}</CLSNAME>
        <LANGU>${language}</LANGU>
        <DESCRIPT>${escapeXml(description)}</DESCRIPT>
        <STATE>1</STATE>
        <CLSCCINCL>X</CLSCCINCL>
        <FIXPT>X</FIXPT>
        <UNICODE>X</UNICODE>
        <ABAP_LANGUAGE_VERSION>5</ABAP_LANGUAGE_VERSION>
      </VSEOCLASS>
    </asx:values>
  </asx:abap>
</abapGit>
`;
}

function escapeXml(value: string): string {
  return value
    .replaceAll(/&/g, '&amp;')
    .replaceAll('<', '&lt;')
    .replaceAll('>', '&gt;')
    .replaceAll('"', '&quot;')
    .replaceAll("'", '&apos;');
}

export async function writeAbapgitLayout(
  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 srcDir = join(outDir, 'src');
  await mkdir(srcDir, { recursive: true });

  const written: string[] = [];

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

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

  const clasXml = join(srcDir, `${base}.clas.xml`);
  await writeFile(
    clasXml,
    buildClasXml(artifact.className, description, language),
    'utf-8',
  );
  written.push(`src/${base}.clas.xml`);

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

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

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

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

function buildIntfXml(
  name: string,
  description: string,
  language: string,
): string {
  return `<?xml version="1.0" encoding="utf-8"?>
<abapGit version="v1.0.0" serializer="LCL_OBJECT_INTF" serializer_version="v1.0.0">
  <asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
    <asx:values>
      <VSEOINTERF>
        <CLSNAME>${name}</CLSNAME>
        <LANGU>${language}</LANGU>
        <DESCRIPT>${escapeXml(description)}</DESCRIPT>
        <EXPOSURE>2</EXPOSURE>
        <STATE>1</STATE>
        <UNICODE>X</UNICODE>
        <ABAP_LANGUAGE_VERSION>5</ABAP_LANGUAGE_VERSION>
      </VSEOINTERF>
    </asx:values>
  </asx:abap>
</abapGit>
`;
}

export async function writeAbapgitInterface(
  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 srcDir = join(outDir, 'src');
  await mkdir(srcDir, { recursive: true });

  const written: string[] = [];

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

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

  const intfXml = join(srcDir, `${base}.intf.xml`);
  await writeFile(
    intfXml,
    buildIntfXml(artifact.name, description, language),
    'utf-8',
  );
  written.push(`src/${base}.intf.xml`);

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