/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import createDebug from 'debug';

const debug = createDebug('platform:aws-ecs');

import {
  AttachRolePolicyCommand,
  CreatePolicyCommand,
  CreateRoleCommand,
  GetRoleCommand,
  IAMClient
} from '@aws-sdk/client-iam';
import sleep from '../../util/sleep.ts';
import ensureS3BucketExists from '../aws/aws-ensure-s3-bucket-exists.ts';
import getAccountId from '../aws/aws-get-account-id.ts';
import { getBucketRegion } from '../aws/aws-get-bucket-region.ts';
import awsGetDefaultRegion from '../aws/aws-get-default-region.ts';
import { S3_BUCKET_NAME_PREFIX } from '../aws/constants.ts';
import { ensureParameterExists } from './legacy/aws-util.ts';

class PlatformECS {
  // Untyped JS class - properties assigned dynamically
  [key: string]: any;

  constructor(
    _script: unknown,
    _payload: unknown,
    opts: Record<string, any>,
    platformOpts: Record<string, any>
  ) {
    this.opts = opts;
    this.platformOpts = platformOpts;

    this.arnPrefx = this.platformOpts.region.startsWith('cn-')
      ? 'arn:aws-cn'
      : 'arn:aws';

    this.testRunId = platformOpts.testRunId;
    if (!this.testRunId) {
      throw new Error('testRunId is required');
    }

    this.s3LifecycleConfigurationRules = [
      {
        Expiration: { Days: 2 },
        Filter: { Prefix: 'tests/' },
        ID: 'RemoveAdHocTestData',
        Status: 'Enabled'
      },
      {
        Expiration: { Days: 7 },
        Filter: { Prefix: 'test-runs/' },
        ID: 'RemoveTestRunMetadata',
        Status: 'Enabled'
      }
    ];
  }

  async init() {
    global.artillery.awsRegion =
      (await awsGetDefaultRegion()) || this.platformOpts.region;

    this.accountId = await getAccountId();

    await ensureSSMParametersExist(this.platformOpts.region);
    const bucketName = await ensureS3BucketExists(
      this.platformOpts.region,
      this.s3LifecycleConfigurationRules,
      false
    );

    global.artillery.s3BucketRegion = await getBucketRegion(bucketName);
    await this.createIAMResources(
      this.accountId,
      this.platformOpts.taskRoleName
    );
  }

  async createIAMResources(accountId: string, taskRoleName: string) {
    const workerRoleArn = await this.createWorkerRole(accountId, taskRoleName);

    return {
      workerRoleArn
    };
  }

  async createWorkerRole(accountId: string, taskRoleName: string) {
    const iam = new IAMClient({ region: global.artillery.awsRegion });

    try {
      const res = await iam.send(
        new GetRoleCommand({ RoleName: taskRoleName })
      );
      return res.Role?.Arn;
    } catch (err) {
      debug(err);
    }

    const createRoleResp = await iam.send(
      new CreateRoleCommand({
        AssumeRolePolicyDocument: JSON.stringify({
          Version: '2012-10-17',
          Statement: [
            {
              Effect: 'Allow',
              Principal: {
                Service: ['ecs-tasks.amazonaws.com', 'ecs.amazonaws.com']
              },
              Action: 'sts:AssumeRole'
            }
          ]
        }),
        Path: '/',
        RoleName: taskRoleName
      })
    );

    const policyDocument = {
      Version: '2012-10-17',
      Statement: [
        {
          Effect: 'Allow',
          Action: ['ssm:DescribeParameters'],
          Resource: ['*']
        },
        {
          Effect: 'Allow',
          Action: [
            'ssm:GetParameters',
            'ssm:GetParameter',
            'ssm:PutParameter',
            'ssm:DeleteParameter',
            'ssm:DescribeParameters',
            'ssm:GetParametersByPath'
          ],
          Resource: [
            `${this.arnPrefx}:ssm:*:${accountId}:parameter/artilleryio/*`
          ]
        },
        {
          Effect: 'Allow',
          Action: ['ecr:GetAuthorizationToken'],
          Resource: ['*']
        },
        {
          Effect: 'Allow',
          Action: ['logs:*'],
          Resource: [
            `${this.arnPrefx}:logs:*:${accountId}:log-group:artilleryio-log-group*:*`
          ]
        },
        {
          Effect: 'Allow',
          Action: ['sqs:*'],
          Resource: [`${this.arnPrefx}:sqs:*:${accountId}:artilleryio*`]
        },
        {
          Effect: 'Allow',
          Action: ['s3:*'],
          Resource: [
            `${this.arnPrefx}:s3:::${S3_BUCKET_NAME_PREFIX}-${accountId}`,
            `${this.arnPrefx}:s3:::${S3_BUCKET_NAME_PREFIX}-${accountId}/*`
          ]
        },
        {
          Effect: 'Allow',
          Action: ['xray:PutTraceSegments', 'xray:PutTelemetryRecords'],
          Resource: ['*']
        }
      ]
    };

    const createPolicyResp = await iam.send(
      new CreatePolicyCommand({
        PolicyName: 'artilleryio-ecs-worker-policy',
        Path: '/',
        PolicyDocument: JSON.stringify(policyDocument)
      })
    );

    await iam.send(
      new AttachRolePolicyCommand({
        PolicyArn: createPolicyResp.Policy?.Arn,
        RoleName: taskRoleName
      })
    );

    debug('Waiting for IAM role to be ready');
    await sleep(30 * 1000);
    return createRoleResp.Role?.Arn;
  }

  async createWorker() {}

  async prepareWorker() {}

  async runWorker() {}

  async stopWorker() {}

  async shutdown() {}
}

async function ensureSSMParametersExist(region: string) {
  await ensureParameterExists(
    '/artilleryio/NPM_TOKEN',
    'null',
    'SecureString',
    region
  );
  await ensureParameterExists(
    '/artilleryio/NPM_REGISTRY',
    'null',
    'String',
    region
  );
  await ensureParameterExists(
    '/artilleryio/NPM_SCOPE',
    'null',
    'String',
    region
  );
  await ensureParameterExists(
    '/artilleryio/ARTIFACTORY_AUTH',
    'null',
    'SecureString',
    region
  );
  await ensureParameterExists(
    '/artilleryio/ARTIFACTORY_EMAIL',
    'null',
    'String',
    region
  );
  await ensureParameterExists(
    '/artilleryio/NPMRC',
    'null',
    'SecureString',
    region
  );
  await ensureParameterExists(
    '/artilleryio/NPM_SCOPE_REGISTRY',
    'null',
    'String',
    region
  );
}

export default PlatformECS;
