import { STS } from "aws-sdk";
import { AWSStepFunctionsHydratorManager, StateMachine, Task } from "flowbject";
import { writeFileSync } from "fs";
import { join, resolve } from "path";
import * as flows from "../flows";

const STATE_MACHINE_PREFIX = "openscript-workflow";
const FUNCTION_NAME_PREFIX = "openscript";
const { STAGE, REGION } = process.env;

async function build(stage: string, region: string) {
  const flowsAsArray = Object.keys(flows).map((name) => ({
    stateMachine: flows[name] as StateMachine,
    name,
  }));

  const stepFunctionHydrator = new AWSStepFunctionsHydratorManager();

  /**
   * Is required to create the actual task resource arn.
   */
  const accountId = await getAccountId(region);

  /**
   * Register task extractor mutator and parse the resource expression
   * to a valid resource-arn.
   */
  stepFunctionHydrator.emitter.addListener(
    "before:extract:state:Task",
    resourceMutator.bind(null, accountId),
  );

  const definitionsArray = flowsAsArray.map(({ stateMachine, name }) => {
    console.log(`Extract "${name}" state machine ...`);
    return {
      name,
      definition: stepFunctionHydrator.extractStateMachine(stateMachine),
    };
  });

  const definitions = definitionsArray.reduce(
    (stepFunctons, { name, definition }) => {
      stepFunctons.stateMachines[name] = {
        name: `${STATE_MACHINE_PREFIX}-${name}-${stage}`,
        definition,
      };
      return stepFunctons;
    },
    { stateMachines: {} },
  );

  const filepath = join(resolve(__dirname, ".."), "stepFunctions.build.json");

  writeFileSync(filepath, JSON.stringify(definitions, null, 4));

  return `
Build process DONE.

Definitions for the serverless-step-functions plugin are created at: '${filepath}',
add the following serverless.yml property: 'stepFunctions: \${file(./stepFunctions.build.json)}'.
    `;
}

function resourceMutator(accountId: string, event: { state: Task }) {
  const [serviceName, functionName] = event.state.getResource().split(":");

  if (serviceName && functionName) {
    const arn = [
      "arn",
      "aws",
      "lambda",
      REGION,
      accountId,
      "function",
      `${FUNCTION_NAME_PREFIX}-${serviceName}-${STAGE}-${functionName}`,
    ].join(":");

    event.state.setResource(arn);
  }
}

async function getAccountId(region: string): Promise<string> {
  const sts = new STS({ region });

  const { Account } = await sts.getCallerIdentity({}).promise();

  if (!Account) {
    throw new Error("Failed to identify account!");
  } else {
    return Account;
  }
}

if (STAGE && REGION) {
  console.log("Build step functions ....");

  build(STAGE, REGION)
    .then((res) => console.log(res))
    .catch((error) => {
      console.error(`StepFunction build error: ${error.message}`);
      process.exit();
    });
} else {
  console.error("Environment variables STAGE and REGION are missing!");
  process.exit(1);
}
