import {
  AutoBeOpenApi,
  AutoBeProgressEventBase,
  AutoBeRealizeCollectorFunction,
} from "@autobe/interface";
import { AutoBeOpenApiTypeChecker } from "@autobe/utils";
import typia, { ILlmApplication, IValidation } from "typia";

import { AutoBeContext } from "../../context/AutoBeContext";
import { AutoBePreliminaryController } from "../common/AutoBePreliminaryController";
import { orchestrateRealizeCorrectOverall } from "./correct/orchestrateRealizeCorrectOverall";
import { transformRealizeCollectorCorrectHistory } from "./histories/transformRealizeCollectorCorrectHistory";
import { AutoBeRealizeCollectorProgrammer } from "./programmers/AutoBeRealizeCollectorProgrammer";
import { IAutoBeRealizeCollectorCorrectApplication } from "./structures/IAutoBeRealizeCollectorCorrectApplication";
import { IAutoBeRealizeFunctionResult } from "./structures/IAutoBeRealizeFunctionResult";

export const orchestrateRealizeCollectorCorrectOverall = async (
  ctx: AutoBeContext,
  props: {
    functions: AutoBeRealizeCollectorFunction[];
    progress: AutoBeProgressEventBase;
  },
): Promise<IAutoBeRealizeFunctionResult<AutoBeRealizeCollectorFunction>[]> => {
  const document: AutoBeOpenApi.IDocument = ctx.state().interface!.document;
  const getNeighbors = (
    func: AutoBeRealizeCollectorFunction,
  ): AutoBeRealizeCollectorFunction[] => {
    const visited: Set<string> = new Set();
    AutoBeOpenApiTypeChecker.visit({
      components: document.components,
      schema: { $ref: `#/components/schemas/${func.plan.dtoTypeName}` },
      closure: (next) => {
        if (AutoBeOpenApiTypeChecker.isReference(next)) {
          const key: string = next.$ref.split("/").pop()!;
          visited.add(key);
        }
      },
    });
    return props.functions.filter(
      (y) =>
        y.plan.dtoTypeName !== func.plan.dtoTypeName &&
        visited.has(y.plan.dtoTypeName),
    );
  };
  return await orchestrateRealizeCorrectOverall(ctx, {
    programmer: {
      location: "src/collectors",

      // Recalculate template for corrected collector function
      template: (func) =>
        AutoBeRealizeCollectorProgrammer.writeTemplate({
          plan: func.plan,
          body: document.components.schemas[func.plan.dtoTypeName],
          model: ctx
            .state()
            .database!.result.data.files.map((f) => f.models)
            .flat()
            .find((m) => m.name === func.plan.databaseSchemaName)!,
          application: ctx.state().database!.result.data,
        }),

      // Replace import statements using Collector-specific programmer
      replaceImportStatements: async (next) => {
        return await AutoBeRealizeCollectorProgrammer.replaceImportStatements(
          ctx,
          {
            dtoTypeName: next.function.plan.dtoTypeName,
            schemas: document.components.schemas,
            code: next.code,
          },
        );
      },

      // No additional files needed for collectors (unlike operations)
      additional: (_functions) => ({}),

      // Create preliminary controller with only databaseSchemas support
      preliminary: (next) =>
        new AutoBePreliminaryController<"databaseSchemas">({
          source: next.source,
          application:
            typia.json.application<IAutoBeRealizeCollectorCorrectApplication>(),
          kinds: ["databaseSchemas"],
          dispatch: (e) => ctx.dispatch(e),
          state: ctx.state(),
          local: {
            databaseSchemas: ctx
              .state()
              .database!.result.data.files.map((f) => f.models)
              .flat()
              .filter((m) => m.name === next.function.plan.databaseSchemaName),
          },
        }),

      // Transform history using Collector-specific transformer
      histories: (next) =>
        transformRealizeCollectorCorrectHistory(ctx, {
          function: next.function,
          neighbors: getNeighbors(next.function),
          failures: next.failures,
          preliminary: next.preliminary,
        }),

      // Create controller with Collector-specific validation
      controller: (next) => {
        const validate: Validator = (input) => {
          const result: IValidation<IAutoBeRealizeCollectorCorrectApplication.IProps> =
            typia.validate<IAutoBeRealizeCollectorCorrectApplication.IProps>(
              input,
            );
          if (result.success === false) return result;
          else if (result.data.request.type !== "write")
            return next.preliminary.validate({
              thinking: result.data.thinking,
              request: result.data.request,
            });

          // Validate collector-specific constraints
          const errors: IValidation.IError[] =
            AutoBeRealizeCollectorProgrammer.validate({
              plan: next.function.plan,
              mappings: result.data.request.mappings,
              neighbors: props.functions.map((f) => f.plan),
              draft: result.data.request.draft,
              revise: result.data.request.revise,
              application: ctx.state().database!.result.data,
            });
          return errors.length
            ? {
                success: false,
                errors,
                data: result.data,
              }
            : result;
        };

        const application: ILlmApplication = next.preliminary.fixApplication(
          typia.llm.application<IAutoBeRealizeCollectorCorrectApplication>({
            validate: {
              process: validate,
            },
          }),
        );
        AutoBeRealizeCollectorProgrammer.fixApplication({
          definition: application,
          application: ctx.state().database!.result.data,
          model: ctx
            .state()
            .database!.result.data.files.map((f) => f.models)
            .flat()
            .find((m) => m.name === next.function.plan.databaseSchemaName)!,
        });
        return {
          protocol: "class",
          name: next.source,
          application,
          execute: {
            process: (v) => {
              if (v.request.type === "write") next.build(v.request);
            },
          } satisfies IAutoBeRealizeCollectorCorrectApplication,
        };
      },
    },
    functions: props.functions,
    progress: props.progress,
  });
};

type Validator = (
  input: unknown,
) => IValidation<IAutoBeRealizeCollectorCorrectApplication.IProps>;
