// Creates the public S3 bucket for brand assets (the email logo) and other files anyone may
// read, using the backend's AWS keys from .env. Everything in it is public: never put tenant
// data (logos, photos, documents) here.
//
//   npm run storage:create-public-bucket                 # bucket "blenaxis-public-assets"
//   npm run storage:create-public-bucket -- my-bucket    # another name
//
// The keys need s3:CreateBucket, s3:PutBucketOwnershipControls, s3:PutBucketPublicAccessBlock,
// s3:PutBucketPolicy and s3:PutObject. Safe to run again: settings are re-applied.
//   1. creates the bucket (if missing) in AWS_REGION with ACLs disabled
//   2. allows a public bucket policy (ACLs stay blocked)
//   3. lets anyone read objects (not list or write)
//   4. uploads the email logo to brand/blenaxis-logo-email.png and checks it's publicly readable
import { readFile } from 'node:fs/promises';
import path from 'node:path';
import {
  CreateBucketCommand, HeadBucketCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand,
  PutObjectCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException,
} from '@aws-sdk/client-s3';
import { env } from '../src/config/env.js';

const bucket = process.argv[2] ?? 'blenaxis-public-assets';
const region = env.AWS_REGION;
const LOGO_KEY = 'brand/blenaxis-logo-email.png';

if (!region || !env.AWS_ACCESS_KEY_ID || !env.AWS_SECRET_ACCESS_KEY) {
  console.error('Set AWS_REGION, AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY first.');
  process.exit(1);
}

const s3 = new S3Client({
  region,
  credentials: { accessKeyId: env.AWS_ACCESS_KEY_ID, secretAccessKey: env.AWS_SECRET_ACCESS_KEY },
});

async function step(label: string, run: () => Promise<unknown>, hint?: string) {
  try {
    await run();
    console.log(`✓ ${label}`);
  } catch (error) {
    const name = error instanceof S3ServiceException ? error.name : '';
    console.error(`✗ ${label}: ${error instanceof Error ? error.message : String(error)}`);
    if (name === 'AccessDenied') console.error('  These AWS keys aren\'t allowed to do this; use keys with the permissions listed at the top of this script.');
    else if (hint) console.error(`  ${hint}`);
    process.exit(1);
  }
}

// 1. Bucket
const exists = await s3.send(new HeadBucketCommand({ Bucket: bucket })).then(
  () => true,
  (error: unknown) => {
    const status = (error as { $metadata?: { httpStatusCode?: number } }).$metadata?.httpStatusCode;
    if (status === 403) {
      console.error(`The name "${bucket}" is taken by another AWS account (or these keys can't see it). Try e.g. "${bucket}-prod".`);
      process.exit(1);
    }
    return false;
  },
);
if (exists) console.log(`• Bucket ${bucket} already exists; updating its settings.`);
else {
  await step(`Created bucket ${bucket} in ${region}`, () =>
    s3.send(new CreateBucketCommand({
      Bucket: bucket,
      ObjectOwnership: 'BucketOwnerEnforced',
      ...(region === 'us-east-1' ? {} : { CreateBucketConfiguration: { LocationConstraint: region as never } }),
    })),
  );
}

await step('ACLs disabled (bucket owner enforced)', () =>
  s3.send(new PutBucketOwnershipControlsCommand({
    Bucket: bucket,
    OwnershipControls: { Rules: [{ ObjectOwnership: 'BucketOwnerEnforced' }] },
  })),
);

// 2. Allow a public policy, keep ACLs blocked.
await step('Public bucket policies allowed (ACLs stay blocked)', () =>
  s3.send(new PutPublicAccessBlockCommand({
    Bucket: bucket,
    PublicAccessBlockConfiguration: {
      BlockPublicAcls: true, IgnorePublicAcls: true, BlockPublicPolicy: false, RestrictPublicBuckets: false,
    },
  })),
);

// 3. Anyone may read objects.
const policy = {
  Version: '2012-10-17',
  Statement: [{ Sid: 'PublicReadObjects', Effect: 'Allow', Principal: '*', Action: 's3:GetObject', Resource: `arn:aws:s3:::${bucket}/*` }],
};
await step('Public read policy added', () =>
  s3.send(new PutBucketPolicyCommand({ Bucket: bucket, Policy: JSON.stringify(policy) })),
  'If this mentions BlockPublicPolicy, Block Public Access is on for the whole AWS account: S3 → "Block Public Access settings for this account" → allow bucket policies, then run again.',
);

// 4. Email logo
const logo = await readFile(path.resolve(import.meta.dirname, '../assets/brand/blenaxis-logo-email.png'));
await step(`Uploaded ${LOGO_KEY}`, () =>
  s3.send(new PutObjectCommand({
    Bucket: bucket, Key: LOGO_KEY, Body: logo, ContentType: 'image/png', CacheControl: 'public, max-age=86400',
  })),
);

const url = `https://${bucket}.s3.${region}.amazonaws.com/${LOGO_KEY}`;
const response = await fetch(url, { method: 'HEAD' });
console.log(response.ok ? `✓ Publicly readable: ${url}` : `! ${url} answered HTTP ${response.status}; check the bucket policy.`);
