import {
  SESv2Client,
  CreateConfigurationSetCommand,
  CreateConfigurationSetEventDestinationCommand,
  UpdateConfigurationSetEventDestinationCommand,
  AlreadyExistsException,
  EventType,
} from "@aws-sdk/client-sesv2";
import { SNSClient, CreateTopicCommand, SubscribeCommand, SetTopicAttributesCommand } from "@aws-sdk/client-sns";

import { env } from "@/config/env";
import { SesInfraBootstrapper } from "./bootstrap-ses-infra-use-case";

const EVENT_DESTINATION_NAME = "app-webhook";

function credentials() {
  return env.awsAccessKeyId && env.awsSecretAccessKey
    ? { accessKeyId: env.awsAccessKeyId, secretAccessKey: env.awsSecretAccessKey }
    : undefined;
}

export class SesInfraBootstrapperImpl implements SesInfraBootstrapper {
  private sesClient = new SESv2Client({ region: env.awsRegion, credentials: credentials() });
  private snsClient = new SNSClient({ region: env.awsRegion, credentials: credentials() });

  async ensureConfigurationSet(name: string): Promise<void> {
    try {
      await this.sesClient.send(new CreateConfigurationSetCommand({ ConfigurationSetName: name }));
    } catch (error) {
      if (!(error instanceof AlreadyExistsException)) throw error;
    }
  }

  async ensureSnsTopic(name: string): Promise<string> {
    const response = await this.snsClient.send(new CreateTopicCommand({ Name: name }));
    if (!response.TopicArn) {
      throw new Error("Falha ao criar/obter o tópico SNS");
    }
    return response.TopicArn;
  }

  async ensureTopicPublishPermission(topicArn: string): Promise<void> {
    const policy = {
      Version: "2012-10-17",
      Statement: [
        {
          Sid: "AllowSesPublish",
          Effect: "Allow",
          Principal: { Service: "ses.amazonaws.com" },
          Action: "SNS:Publish",
          Resource: topicArn,
        },
      ],
    };

    await this.snsClient.send(
      new SetTopicAttributesCommand({
        TopicArn: topicArn,
        AttributeName: "Policy",
        AttributeValue: JSON.stringify(policy),
      })
    );
  }

  async ensureHttpsSubscription(topicArn: string, endpoint: string): Promise<void> {
    await this.snsClient.send(
      new SubscribeCommand({ TopicArn: topicArn, Protocol: "https", Endpoint: endpoint })
    );
  }

  async ensureEventDestination(configurationSetName: string, topicArn: string): Promise<void> {
    const eventDestination = {
      Enabled: true,
      MatchingEventTypes: [
        EventType.SEND,
        EventType.DELIVERY,
        EventType.BOUNCE,
        EventType.COMPLAINT,
        EventType.REJECT,
        EventType.OPEN,
        EventType.CLICK,
      ],
      SnsDestination: { TopicArn: topicArn },
    };

    try {
      await this.sesClient.send(
        new CreateConfigurationSetEventDestinationCommand({
          ConfigurationSetName: configurationSetName,
          EventDestinationName: EVENT_DESTINATION_NAME,
          EventDestination: eventDestination,
        })
      );
    } catch (error) {
      if (!(error instanceof AlreadyExistsException)) throw error;

      await this.sesClient.send(
        new UpdateConfigurationSetEventDestinationCommand({
          ConfigurationSetName: configurationSetName,
          EventDestinationName: EVENT_DESTINATION_NAME,
          EventDestination: eventDestination,
        })
      );
    }
  }
}
