/**
 * 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 mustache from 'mustache';
import Case from 'case';
import { TypeAnnotation } from '../core';

const TEMPLATE = `
/**
{{#codegenNoticeLines}}
 * {{{line}}}
{{/codegenNoticeLines}}
 */

#pragma once

#include "RNOH/CppComponentInstance.h"
#include <react/renderer/components/{{libraryName}}/ShadowNodes.h>

namespace rnoh {
  class Base{{name}}ComponentInstance
      : public CppComponentInstance<facebook::react::{{name}}ShadowNode> {
  public:
    using CppComponentInstance::CppComponentInstance;

    void handleCommand(std::string const &commandName, folly::dynamic const &args) override {
      {{#commands}}
      if (commandName == "{{commandName}}") {
        this->on{{CommandName}}Command({{argsFromDynamic}});
      }
      {{/commands}}
    }
    
    {{#commands}}
    virtual void on{{CommandName}}Command({{argsDeclaration}}) = 0;
    {{/commands}}
  };
} // namespace rnoh
`;

type BaseComponentInstanceHCommand = {
  name: string;
  args: { name: string; typeAnnotation: TypeAnnotation }[];
};

export class BaseComponentInstanceHTemplate {
  private commands: BaseComponentInstanceHCommand[] = [];

  constructor(
    private componentName: string,
    private libraryName: string,
    private codegenNoticeLines: string[]
  ) {}

  addCommand(command: BaseComponentInstanceHCommand) {
    this.commands.push(command);
  }

  build(): string {
    return mustache.render(TEMPLATE, {
      codegenNoticeLines: this.codegenNoticeLines.map((line) => ({
        line,
      })),
      name: this.componentName,
      libraryName: this.libraryName,
      commands: this.commands.map(({ name, args }) => {
        return {
          commandName: name,
          CommandName: Case.pascal(name),
          argsDeclaration: args
            .map(
              ({ name, typeAnnotation }) =>
                `${cppTypeFromTypeAnnotation(typeAnnotation)} ${name}`
            )
            .join(', '),
          argsFromDynamic: args
            .map(
              ({ typeAnnotation }, index) =>
                `args[${index}]${dynamicExtractorFromTypeAnnotation(
                  typeAnnotation
                )}`
            )
            .join(', '),
        };
      }),
    });
  }
}

function cppTypeFromTypeAnnotation(typeAnnotation: TypeAnnotation): string {
  switch (typeAnnotation.type) {
    case 'BooleanTypeAnnotation':
      return 'bool';
    case 'Int32TypeAnnotation':
      return 'int';
    case 'FloatTypeAnnotation':
      return 'float';
    case 'DoubleTypeAnnotation':
      return 'double';
    case 'StringTypeAnnotation':
      return 'std::string';
    default:
      return 'folly::dynamic';
  }
}

function dynamicExtractorFromTypeAnnotation(
  typeAnnotation: TypeAnnotation
): string {
  switch (typeAnnotation.type) {
    case 'BooleanTypeAnnotation':
      return '.asBool()';
    case 'Int32TypeAnnotation':
      return '.asInt()';
    case 'FloatTypeAnnotation':
      return '.asDouble()';
    case 'DoubleTypeAnnotation':
      return '.asDouble()';
    case 'StringTypeAnnotation':
      return '.asString()';
    default:
      return '';
  }
}