import { IMicroAgenticaHistoryJson } from "@agentica/core";
import {
  AutoBeOpenApi,
  AutoBePreliminaryKind,
  AutoBeRealizeAuthorization,
  AutoBeRealizeCollectorFunction,
  AutoBeRealizeTransformerFunction,
} from "@autobe/interface";
import { StringUtil } from "@autobe/utils";
import { v7 } from "uuid";

import { AutoBeSystemPromptConstant } from "../../../constants/AutoBeSystemPromptConstant";
import { AutoBeTemplateFileConstant } from "../../../constants/AutoBeTemplateFileConstant";
import { AutoBeState } from "../../../context/AutoBeState";
import { IAutoBeOrchestrateHistory } from "../../../structures/IAutoBeOrchestrateHistory";
import { AutoBePreliminaryController } from "../../common/AutoBePreliminaryController";
import { AutoBeRealizeOperationProgrammer } from "../programmers/AutoBeRealizeOperationProgrammer";
import { IAutoBeRealizeScenarioResult } from "../structures/IAutoBeRealizeScenarioResult";
import { getRealizeWriteInputType } from "../utils/getRealizeWriteInputType";
import { transformRealizeWriteMembershipHistory } from "./transformRealizeWriteMembershipHistory";

export const transformRealizeOperationWriteHistory = <
  K extends AutoBePreliminaryKind,
>(props: {
  state: AutoBeState;
  scenario: IAutoBeRealizeScenarioResult;
  authorization: AutoBeRealizeAuthorization | null;
  totalAuthorizations: AutoBeRealizeAuthorization[];
  collectors: AutoBeRealizeCollectorFunction[];
  transformers: AutoBeRealizeTransformerFunction[];
  dto: Record<string, string>;
  preliminary: AutoBePreliminaryController<K>;
}): IAutoBeOrchestrateHistory => {
  const payloads: Record<string, string> = Object.fromEntries(
    props.totalAuthorizations.map((el) => [
      el.payload.location,
      el.payload.content,
    ]),
  );
  const operation: AutoBeOpenApi.IOperation = props.scenario.operation;
  const authorizationHistories: IMicroAgenticaHistoryJson[] =
    operation.authorizationType
      ? transformRealizeWriteMembershipHistory(operation, payloads)
      : [];
  const document: AutoBeOpenApi.IDocument = props.state.interface!.document;

  return {
    histories: [
      {
        id: v7(),
        created_at: new Date().toISOString(),
        type: "systemMessage",
        text: AutoBeSystemPromptConstant.REALIZE_OPERATION_WRITE,
      },
      ...props.preliminary.getHistories(),
      ...authorizationHistories,
      {
        id: v7(),
        created_at: new Date().toISOString(),
        type: "systemMessage",
        text: AutoBeSystemPromptConstant.REALIZE_OPERATION_WRITE_ARTIFACT.replaceAll(
          `{{TEMPLATE}}`,
          getRealizeWriteInputType(operation, props.authorization),
        )
          .replaceAll(`{{DTO}}`, JSON.stringify(props.dto))
          .replaceAll(
            "{{MyGlobal}}",
            AutoBeTemplateFileConstant["realize-of-postgres/src/MyGlobal.ts"],
          ),
      },
      {
        id: v7(),
        type: "systemMessage",
        created_at: new Date().toISOString(),
        text: AutoBeRealizeOperationProgrammer.writeTemplate({
          authorizations: props.totalAuthorizations,
          schemas: document.components.schemas,
          operation,
          collectors: props.collectors,
          transformers: props.transformers,
        }),
      },
      {
        id: v7(),
        type: "systemMessage",
        created_at: new Date().toISOString(),
        text: StringUtil.trim`
          Write new code based on the following operation.

          \`\`\`json
          ${JSON.stringify(props.scenario)}
          \`\`\`
        `,
      },
      {
        id: v7(),
        created_at: new Date().toISOString(),
        type: "assistantMessage",
        text: StringUtil.trim`
          I understand your request.

          To summarize:
          - I must **never use the native \`Date\` type** in any code or type definitions.
          - Instead, all date and datetime values must be handled as \`string & tags.Format<'date-time'>\`.
          - This rule is **strict** and applies everywhere, including domain types, API inputs/outputs, and Prisma models.
          - Even if a library or tool returns a \`Date\`, I must convert it to the correct string format before use.

          Especially regarding the \`Date\` type: I understand that using it can lead to type inconsistency and runtime issues, so I will completely avoid it in all circumstances.

          I'll make sure to follow all these rules strictly. Let's proceed with this in mind.
        `,
      },
    ],
    userMessage: StringUtil.trim`
      Write implementation for ${operation.method.toUpperCase()} ${operation.path} please.

      Write the complete, production-ready TypeScript code that strictly follows these rules:

      DO NOT:
      - Use the native \`Date\` type anywhere
      - Use \`as\` for type assertions

      DO:
      - Write all date/datetime values as \`string & tags.Format<'date-time'>\`
      - Generate UUIDs using \`v4()\` and type as \`string & tags.Format<'uuid'>\`
      - Resolve types properly without assertions
      - Type all functions with clear parameter and return types
      6. Do not skip validations or default values where necessary.
      7. Follow functional, immutable, and consistent code structure.

      Use \`@nestia/e2e\` test structure if relevant.
    `,
  };
};
