import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

const {
  MockBlobServiceClient,
  mockFromConnectionString,
  mockGetContainerClient,
  MockStorageSharedKeyCredential,
  mockGenerateBlobSASQueryParameters,
} = vi.hoisted(() => {
  const mockGetContainerClient = vi.fn(() => ({
    createIfNotExists: vi.fn(),
  }));

  const mockFromConnectionString = vi.fn(() => ({
    getContainerClient: mockGetContainerClient,
  }));

  const MockBlobServiceClient = vi.fn(() => ({
    getContainerClient: mockGetContainerClient,
  })) as unknown as {
    new (...args: any[]): any;
    fromConnectionString: typeof mockFromConnectionString;
  };

  (MockBlobServiceClient as any).fromConnectionString = mockFromConnectionString;

  const MockStorageSharedKeyCredential = vi.fn(() => ({ kind: 'shared-key-cred' }));

  const mockGenerateBlobSASQueryParameters = vi.fn(() => ({
    toString: () => 'sig=fake',
  }));

  return {
    MockBlobServiceClient,
    mockFromConnectionString,
    mockGetContainerClient,
    MockStorageSharedKeyCredential,
    mockGenerateBlobSASQueryParameters,
  };
});

vi.mock('@azure/storage-blob', () => ({
  BlobServiceClient: MockBlobServiceClient,
  ContainerClient: class {},
  StorageSharedKeyCredential: MockStorageSharedKeyCredential,
  BlobSASPermissions: class {
    read = false;
    write = false;
    create = false;
    delete = false;
  },
  generateBlobSASQueryParameters: mockGenerateBlobSASQueryParameters,
  SASProtocol: { Https: 'Https' },
}));

import {
  _resetBlobClient,
  generateSasUrl,
  getBlobServiceClient,
  getContainerClient,
  isBlobStorageConfigured,
} from '../index.js';

describe('blob', () => {
  beforeEach(() => {
    _resetBlobClient();
    MockBlobServiceClient.mockClear();
    mockFromConnectionString.mockClear();
    mockGetContainerClient.mockClear();
    MockStorageSharedKeyCredential.mockClear();
    mockGenerateBlobSASQueryParameters.mockClear();

    delete process.env.AZURE_BLOB_CONNECTION_STRING;
    delete process.env.AZURE_BLOB_ACCOUNT_NAME;
    delete process.env.AZURE_BLOB_ACCOUNT_KEY;
  });

  afterEach(() => {
    delete process.env.AZURE_BLOB_CONNECTION_STRING;
    delete process.env.AZURE_BLOB_ACCOUNT_NAME;
    delete process.env.AZURE_BLOB_ACCOUNT_KEY;
  });

  it('isBlobStorageConfigured is false when unset', () => {
    expect(isBlobStorageConfigured()).toBe(false);
  });

  it('isBlobStorageConfigured is true when connection string is set', () => {
    process.env.AZURE_BLOB_CONNECTION_STRING = 'AccountName=x;AccountKey=y;';
    expect(isBlobStorageConfigured()).toBe(true);
  });

  it('getBlobServiceClient uses connection string when present', () => {
    process.env.AZURE_BLOB_CONNECTION_STRING = 'AccountName=acc;AccountKey=key==;';
    const c1 = getBlobServiceClient();
    const c2 = getBlobServiceClient();
    expect(c1).toBe(c2);
    expect(mockFromConnectionString).toHaveBeenCalledTimes(1);
    expect(MockBlobServiceClient).not.toHaveBeenCalled(); // should not call constructor in this path
  });

  it('getBlobServiceClient uses account name + key when provided', () => {
    process.env.AZURE_BLOB_ACCOUNT_NAME = 'acc';
    process.env.AZURE_BLOB_ACCOUNT_KEY = 'key==';

    getBlobServiceClient();

    expect(MockStorageSharedKeyCredential).toHaveBeenCalledWith('acc', 'key==');
    expect(MockBlobServiceClient).toHaveBeenCalledWith(
      'https://acc.blob.core.windows.net',
      expect.anything()
    );
  });

  it('getBlobServiceClient throws when credentials missing', () => {
    expect(() => getBlobServiceClient()).toThrow('Azure Blob Storage not configured');
  });

  it('getContainerClient creates container and caches client', async () => {
    process.env.AZURE_BLOB_CONNECTION_STRING = 'AccountName=acc;AccountKey=key==;';

    const c1 = await getContainerClient('audio');
    const c2 = await getContainerClient('audio');
    expect(c1).toBe(c2);

    expect(mockGetContainerClient).toHaveBeenCalledTimes(1);
    expect(c1.createIfNotExists).toHaveBeenCalledTimes(1);
  });

  it('generateSasUrl uses parsed connection string when account vars not set', () => {
    process.env.AZURE_BLOB_CONNECTION_STRING = 'AccountName=acc;AccountKey=key==;';

    const url = generateSasUrl('audio', 'path/file.wav', 'rw', 10);
    expect(url).toContain('https://acc.blob.core.windows.net/audio/path/file.wav?');
    expect(mockGenerateBlobSASQueryParameters).toHaveBeenCalledTimes(1);
  });

  it('generateSasUrl prefers explicit account vars when provided', () => {
    process.env.AZURE_BLOB_CONNECTION_STRING = 'AccountName=fromconn;AccountKey=key==;';
    process.env.AZURE_BLOB_ACCOUNT_NAME = 'acc';
    process.env.AZURE_BLOB_ACCOUNT_KEY = 'key==';

    const url = generateSasUrl('audio', 'x', 'r', 1);
    expect(url).toContain('https://acc.blob.core.windows.net/audio/x?');
  });
});
