/**
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Case from 'case';
import { AbsolutePath } from '../../core';
import { CodeGenerator } from '../core';
import { IndexTSTemplate, RNOHGeneratedPackageHTemplate } from '../templates';
import { BasePackageHTemplate } from '../templates/BasePackageHTemplate';

export type GlueCodeComponentDataV1 = { name: string; eventNames: string[] };

export type GlueCodeComponentDataV2 = { name: string; libraryCppName: string };

export type GlueCodeTurboModuleData = { name: string };

export type GlueCodeDataV1 = {
  components: GlueCodeComponentDataV1[];
  turboModules: GlueCodeTurboModuleData[];
};

type LibraryName = string;
export type GlueCodeDataV2 = Map<
  LibraryName,
  {
    components: GlueCodeComponentDataV2[];
    turboModules: GlueCodeTurboModuleData[];
  }
>;

export type GlueCodeData = {
  v1: GlueCodeDataV1;
  v2: GlueCodeDataV2;
};

/**
 * Generates code based on UberGenerators results. If UberGenerators produce "leaves" (concrete turbo module or component classes),
 * this class generates "branches" (barrel files like index.ts) and "trunks" (package file or files).
 */
export class AppBuildTimeGlueCodeGenerator
  implements CodeGenerator<GlueCodeData>
{
  constructor(
    private cppOutputPath: AbsolutePath,
    private etsOutputPath: AbsolutePath,
    private codegenNoticeLines: string[]
  ) {}

  generate({ v1, v2 }: GlueCodeData): Map<AbsolutePath, string> {
    const fileContentByPath = new Map<AbsolutePath, string>();
    const generatedPackageH_v1 = new RNOHGeneratedPackageHTemplate();
    const components_indexTS = new IndexTSTemplate(this.codegenNoticeLines);
    const turboModules_indexTS = new IndexTSTemplate(this.codegenNoticeLines);
    const generated_indexETS = new IndexTSTemplate(this.codegenNoticeLines);

    generated_indexETS.addReexport({ from: './ts' });
    v1.components.forEach(({ name, eventNames }) => {
      components_indexTS.addReexport({ from: `./${name}` });
      generatedPackageH_v1.addComponent({
        name,
        supportedEventNames: eventNames,
        isArkTSComponent: true,
      });
    });

    v1.turboModules.forEach((turboModule) => {
      turboModules_indexTS.addReexport({ from: `./${turboModule.name}` });
      generatedPackageH_v1.addTurboModule(turboModule);
    });
    v2.forEach(({ components, turboModules }, libraryName) => {
      const basePackageClassName = `Base${Case.pascal(libraryName)}Package`;
      const generatedPackageH_v2 = new BasePackageHTemplate(
        basePackageClassName,
        this.codegenNoticeLines
      );
      components.forEach((component) => {
        generatedPackageH_v2.addComponent({
          ...component,
          isArkTSComponent: false,
        });
      });
      turboModules.forEach((turboModule) => {
        turboModules_indexTS.addReexport({ from: `./${turboModule.name}` });
        generatedPackageH_v2.addTurboModule(turboModule);
      });
      fileContentByPath.set(
        this.cppOutputPath.copyWithNewSegment(
          libraryName,
          'RNOH',
          'generated',
          `${basePackageClassName}.h`
        ),
        generatedPackageH_v2.build()
      );
    });
    fileContentByPath.set(
      this.cppOutputPath.copyWithNewSegment('RNOHGeneratedPackage.h'),
      generatedPackageH_v1.build()
    );
    fileContentByPath.set(
      this.etsOutputPath.copyWithNewSegment('ts.ts'),
      new IndexTSTemplate(this.codegenNoticeLines)
        .addReexport({ from: './components/ts', as: 'RNC' })
        .addReexport({ from: './turboModules/ts', as: 'TM' })
        .build()
    );
    fileContentByPath.set(
      this.etsOutputPath.copyWithNewSegment('components', 'ts.ts'),
      components_indexTS.build()
    );
    fileContentByPath.set(
      this.etsOutputPath.copyWithNewSegment('turboModules', 'ts.ts'),
      turboModules_indexTS.build()
    );
    fileContentByPath.set(
      this.etsOutputPath.copyWithNewSegment('index.ets'),
      generated_indexETS.build()
    );
    return fileContentByPath;
  }
}