import * as cdk from "aws-cdk-lib";
import {
  AnyPrincipal,
  Effect,
  IRole,
  ManagedPolicy,
  PolicyStatement,
  Role,
  ServicePrincipal,
} from "aws-cdk-lib/aws-iam";
import { Bucket } from "aws-cdk-lib/aws-s3";
import { Construct } from "constructs";
import { ConfigStack, ConfigStackProps } from "./config-stack";
import { regions } from "./const";
import { generateTemplateBody } from "./util";

interface StackSetProps extends cdk.StackProps {
  readonly accountAlias: string;
  readonly centralRegion: string;
  readonly administrationRole: IRole;
  readonly executionRole: IRole;
}

export class ConfigStackSet extends cdk.Stack {
  constructor(scope: Construct, id: string, props: StackSetProps) {
    super(scope, id, props);

    const { accountId } = new cdk.ScopedAws(this);
    const { accountAlias, centralRegion, administrationRole, executionRole } = props;

    const roleName = "ConfigRecorderRole";

    new Role(this, "ConfigRecorderRole", {
      roleName,
      assumedBy: new ServicePrincipal("config.amazonaws.com"),
      managedPolicies: [ManagedPolicy.fromAwsManagedPolicyName("service-role/AWS_ConfigRole")],
    });

    const bucketName = `${accountAlias}-config`;

    const configBucket = new Bucket(this, "configBucket", {
      bucketName,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
      enforceSSL: true,
      lifecycleRules: [
        {
          id: "config-rule",
          expiration: cdk.Duration.days(550),
          enabled: true,
        },
      ],
    });
    configBucket.addToResourcePolicy(
      new PolicyStatement({
        effect: Effect.ALLOW,
        principals: [new ServicePrincipal("config.amazonaws.com")],
        actions: ["s3:GetBucketAcl", "s3:PutObject"],
        resources: [`${configBucket.bucketArn}`, `${configBucket.bucketArn}/*`],
      }),
    );
    configBucket.addToResourcePolicy(
      new PolicyStatement({
        effect: Effect.DENY,
        principals: [new AnyPrincipal()],
        actions: ["s3:*"],
        resources: [`${configBucket.bucketArn}`, `${configBucket.bucketArn}/*`],
        conditions: {
          Bool: {
            "aws:SecureTransport": false,
          },
        },
      }),
    );

    const templateBody = generateTemplateBody<ConfigStackProps>(ConfigStack, {
      centralRegion,
      bucketName,
      roleName,
    });

    new cdk.CfnStackSet(this, "ConfigStackSet", {
      stackSetName: `config-stacks`,
      administrationRoleArn: administrationRole.roleArn,
      executionRoleName: executionRole.roleName,
      permissionModel: "SELF_MANAGED",
      capabilities: ["CAPABILITY_NAMED_IAM"],
      operationPreferences: {
        regionConcurrencyType: "PARALLEL",
      },
      stackInstancesGroup: [
        {
          regions: regions,
          deploymentTargets: {
            accounts: [accountId],
          },
        },
      ],
      templateBody,
    });
  }
}
