import { assertEquals, assertRejects } from 'jsr:@std/assert';
import { describe, it } from 'jsr:@std/testing/bdd';
import { TypedRequest } from '@api.global/typedrequest';
import {
  createTestContainer,
  getTestPorts,
  loginAndGetIdentity,
} from './helpers/server.helper.ts';
import { ObjectStorageContainer } from '../ts/index.ts';
import type { IReq_AdminLogout, IReq_VerifyIdentity } from '../ts_interfaces/requests/admin.ts';
import type { IReq_CreateBucket } from '../ts_interfaces/requests/buckets.ts';
import type { IReq_AddCredential } from '../ts_interfaces/requests/credentials.ts';
import type { IReq_ListAuditEntries } from '../ts_interfaces/requests/audit.ts';

const PORT_INDEX = 12;
const ports = getTestPorts(PORT_INDEX);
const url = `http://localhost:${ports.uiPort}/typedrequest`;
const storageDirectory = `.nogit/testdata-${PORT_INDEX}`;

const cleanupStorageDirectory = async () => {
  try {
    await Deno.remove(storageDirectory, { recursive: true });
  } catch (error) {
    if (!(error instanceof Deno.errors.NotFound)) {
      throw error;
    }
  }
};

describe('Enterprise hardening', { sanitizeResources: false, sanitizeOps: false }, () => {
  it('refuses default admin credentials on persistent production storage', async () => {
    const previousAllowInsecureDefaults = Deno.env.get('OBJST_ALLOW_INSECURE_DEFAULTS');
    const previousAccessKey = Deno.env.get('OBJST_ACCESS_KEY');
    const previousSecretKey = Deno.env.get('OBJST_SECRET_KEY');
    const previousAdminPassword = Deno.env.get('OBJST_ADMIN_PASSWORD');
    const previousStorageDir = Deno.env.get('OBJST_STORAGE_DIR');
    try {
      Deno.env.delete('OBJST_ALLOW_INSECURE_DEFAULTS');
      Deno.env.delete('OBJST_ACCESS_KEY');
      Deno.env.delete('OBJST_SECRET_KEY');
      Deno.env.delete('OBJST_ADMIN_PASSWORD');
      Deno.env.delete('OBJST_STORAGE_DIR');
      const container = new ObjectStorageContainer();
      await assertRejects(() => container.start(), Error, 'Refusing to start with default admin credentials');
    } finally {
      if (previousAllowInsecureDefaults === undefined) {
        Deno.env.delete('OBJST_ALLOW_INSECURE_DEFAULTS');
      } else {
        Deno.env.set('OBJST_ALLOW_INSECURE_DEFAULTS', previousAllowInsecureDefaults);
      }
      if (previousAccessKey === undefined) Deno.env.delete('OBJST_ACCESS_KEY');
      else Deno.env.set('OBJST_ACCESS_KEY', previousAccessKey);
      if (previousSecretKey === undefined) Deno.env.delete('OBJST_SECRET_KEY');
      else Deno.env.set('OBJST_SECRET_KEY', previousSecretKey);
      if (previousAdminPassword === undefined) Deno.env.delete('OBJST_ADMIN_PASSWORD');
      else Deno.env.set('OBJST_ADMIN_PASSWORD', previousAdminPassword);
      if (previousStorageDir === undefined) Deno.env.delete('OBJST_STORAGE_DIR');
      else Deno.env.set('OBJST_STORAGE_DIR', previousStorageDir);
    }
  });

  it('exposes health endpoints and writes audit entries for privileged actions', async () => {
    await cleanupStorageDirectory();
    let container: ObjectStorageContainer | null = null;

    try {
      container = createTestContainer(PORT_INDEX, { storageDirectory });
      await container.start();

      const live = await fetch(`http://localhost:${ports.uiPort}/livez`);
      assertEquals(live.status, 200);
      assertEquals((await live.json()).status, 'alive');

      const ready = await fetch(`http://localhost:${ports.uiPort}/readyz`);
      assertEquals(ready.status, 200);
      assertEquals((await ready.json()).status, 'ready');

      const health = await fetch(`http://localhost:${ports.uiPort}/healthz`);
      assertEquals(health.status, 200);
      assertEquals((await health.json()).ok, true);

      const metrics = await fetch(`http://localhost:${ports.uiPort}/metrics`);
      assertEquals(metrics.status, 200);
      assertEquals((await metrics.text()).includes('objectstorage_ready 1'), true);

      const identity = await loginAndGetIdentity(ports.uiPort);
      const createBucket = new TypedRequest<IReq_CreateBucket>(url, 'createBucket');
      await createBucket.fire({ identity, bucketName: 'enterprise-audit-bucket' });

      const addCredential = new TypedRequest<IReq_AddCredential>(url, 'addCredential');
      await addCredential.fire({
        identity,
        accessKeyId: 'enterprise-key',
        secretAccessKey: 'enterprise-secret',
      });

      const adminConfigInfo = await Deno.stat(
        `${storageDirectory}/.objectstorage/admin-config.json`,
      );
      if (adminConfigInfo.mode !== null) {
        assertEquals(adminConfigInfo.mode & 0o777, 0o600);
      }

      const listAuditEntries = new TypedRequest<IReq_ListAuditEntries>(url, 'listAuditEntries');
      const auditResponse = await listAuditEntries.fire({ identity, limit: 10 });
      const actions = auditResponse.entries.map((entry) => entry.action);

      assertEquals(actions.includes('admin.login'), true);
      assertEquals(actions.includes('bucket.create'), true);
      assertEquals(actions.includes('credential.add'), true);
    } finally {
      if (container) {
        await container.stop();
      }
      await cleanupStorageDirectory();
    }
  });

  it('revokes admin identities on logout', async () => {
    await cleanupStorageDirectory();
    let container: ObjectStorageContainer | null = null;

    try {
      container = createTestContainer(PORT_INDEX, { storageDirectory });
      await container.start();

      const identity = await loginAndGetIdentity(ports.uiPort);
      const logout = new TypedRequest<IReq_AdminLogout>(url, 'adminLogout');
      await logout.fire({ identity });

      const verifyIdentity = new TypedRequest<IReq_VerifyIdentity>(url, 'verifyIdentity');
      const verification = await verifyIdentity.fire({ identity });
      assertEquals(verification.valid, false);

      const createBucket = new TypedRequest<IReq_CreateBucket>(url, 'createBucket');
      await assertRejects(() =>
        createBucket.fire({ identity, bucketName: 'revoked-token-bucket' })
      );
    } finally {
      if (container) {
        await container.stop();
      }
      await cleanupStorageDirectory();
    }
  });
});
