import { mkdir, writeFile, unlink } from "node:fs/promises";
import path from "node:path";

export type UploadAssetInput = {
  tenantId: string;
  projectId?: string;
  assetId: string;
  body: Buffer | Uint8Array;
  mimeType: string;
  fileName: string;
};

export type StorageObject = {
  storageKey: string;
  size: number;
  mimeType: string;
};

export interface StorageService {
  buildTenantKey(input: {
    tenantId: string;
    projectId?: string;
    assetId: string;
    fileName: string;
  }): string;
  uploadAsset(input: UploadAssetInput): Promise<StorageObject>;
  deleteAsset(storageKey: string, tenantId: string): Promise<void>;
  getSignedUrl(
    storageKey: string,
    tenantId: string,
    expiresInSeconds?: number,
  ): Promise<string>;
}

function assertTenantOwnsKey(storageKey: string, tenantId: string): void {
  const expectedPrefix = `tenants/${tenantId}/`;
  if (!storageKey.startsWith(expectedPrefix)) {
    throw new Error("Storage key does not belong to tenant");
  }
}

/**
 * Local filesystem storage for development.
 * Keys always under tenants/<tenantId>/…
 */
export class LocalStorageService implements StorageService {
  private root = path.join(process.cwd(), ".data", "storage");

  buildTenantKey(input: {
    tenantId: string;
    projectId?: string;
    assetId: string;
    fileName: string;
  }): string {
    const safeName = input.fileName.replace(/[^a-zA-Z0-9._-]/g, "_");
    if (input.projectId) {
      return `tenants/${input.tenantId}/projects/${input.projectId}/media/${input.assetId}-${safeName}`;
    }
    return `tenants/${input.tenantId}/assets/${input.assetId}-${safeName}`;
  }

  async uploadAsset(input: UploadAssetInput): Promise<StorageObject> {
    const storageKey = this.buildTenantKey(input);
    assertTenantOwnsKey(storageKey, input.tenantId);

    const absolute = path.join(this.root, storageKey);
    await mkdir(path.dirname(absolute), { recursive: true });
    await writeFile(absolute, input.body);

    return {
      storageKey,
      size: input.body.byteLength,
      mimeType: input.mimeType,
    };
  }

  async deleteAsset(storageKey: string, tenantId: string): Promise<void> {
    assertTenantOwnsKey(storageKey, tenantId);
    const absolute = path.join(this.root, storageKey);
    try {
      await unlink(absolute);
    } catch {
      // Missing file is fine for idempotent deletes
    }
  }

  async getSignedUrl(
    storageKey: string,
    tenantId: string,
    _expiresInSeconds = 300,
  ): Promise<string> {
    assertTenantOwnsKey(storageKey, tenantId);
    return `/api/media/file?key=${encodeURIComponent(storageKey)}`;
  }

  resolveAbsolutePath(storageKey: string, tenantId: string): string {
    assertTenantOwnsKey(storageKey, tenantId);
    return path.join(this.root, storageKey);
  }
}

const globalForStorage = globalThis as unknown as {
  archiStorage?: StorageService;
};

export function getStorage(): StorageService {
  if (!globalForStorage.archiStorage) {
    globalForStorage.archiStorage = new LocalStorageService();
  }
  return globalForStorage.archiStorage;
}
