import * as cdk from "aws-cdk-lib";
import * as route53 from "aws-cdk-lib/aws-route53";
import * as ses from "aws-cdk-lib/aws-ses";
import * as ssm from "aws-cdk-lib/aws-ssm";
import * as cr from "aws-cdk-lib/custom-resources";
import { Construct } from "constructs";

export type SesProps = {
  readonly fromEmail: string;
};

interface StackProps extends cdk.StackProps {
  readonly sesProps: SesProps;
}

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

    const { fromEmail } = props.sesProps;
    const hostedZoneId = ssm.StringParameter.valueForStringParameter(this, "/account/hosted-zone-id");
    const hostedZoneName = ssm.StringParameter.valueForStringParameter(this, "/account/hosted-zone-name");

    const zone = route53.PublicHostedZone.fromPublicHostedZoneAttributes(this, "Zone", {
      hostedZoneId,
      zoneName: hostedZoneName,
    });

    new ses.EmailIdentity(this, "DomainEmailIdentity", {
      identity: ses.Identity.publicHostedZone(zone),
    });

    const senderEmailIdentity = new ses.EmailIdentity(this, "SenderEmailIdentity", {
      identity: ses.Identity.email(fromEmail),
    });

    // ポリシーの追加はCDKで直接サポートされていないため、カスタムリソースを使用して実装
    const policyName = "AllowAccountToSendFromEmail";
    const policyDocument = JSON.stringify({
      Version: "2012-10-17",
      Statement: [
        {
          Effect: "Allow",
          Principal: { AWS: `arn:aws:iam::${this.account}:root` },
          Action: "ses:SendEmail",
          Resource: senderEmailIdentity.emailIdentityArn,
          Condition: { StringEquals: { "ses:FromAddress": fromEmail } },
        },
      ],
    });
    new cr.AwsCustomResource(this, "SesIdentityPolicy", {
      onCreate: {
        service: "SES",
        action: "putIdentityPolicy",
        parameters: {
          Identity: fromEmail,
          PolicyName: policyName,
          Policy: policyDocument,
        },
        physicalResourceId: cr.PhysicalResourceId.of(`${fromEmail}-${policyName}`),
      },
      onUpdate: {
        service: "SES",
        action: "putIdentityPolicy",
        parameters: {
          Identity: fromEmail,
          PolicyName: policyName,
          Policy: policyDocument,
        },
        physicalResourceId: cr.PhysicalResourceId.of(`${fromEmail}-${policyName}`),
      },
      onDelete: {
        service: "SES",
        action: "deleteIdentityPolicy",
        parameters: { Identity: fromEmail, PolicyName: policyName },
      },
      policy: cr.AwsCustomResourcePolicy.fromStatements([
        new cdk.aws_iam.PolicyStatement({
          actions: ["ses:PutIdentityPolicy", "ses:DeleteIdentityPolicy"],
          resources: ["*"], // PutIdentityPolicyおよびDeleteIdentityPolicyはリソースレベルの権限をサポートしていないため、ワイルドカードを使用 https://docs.aws.amazon.com/ja_jp/service-authorization/latest/reference/list_amazonses.html#amazonses-actions-as-permissions
        }),
      ]),
    });
  }
}
