import '@angular/compiler';
import {
  beforeEach,
  describe,
  expect,
  it,
  vi
} from 'vitest';
import {
  Injector,
  runInInjectionContext,
  signal
} from '@angular/core';
import { Store } from '@ngrx/store';

import { DatabaseService } from '../../../../infrastructure/persistence';
import { AttachmentStorageService } from '../../infrastructure/services/attachment-storage.service';
import { AttachmentPersistenceService } from './attachment-persistence.service';
import { AttachmentRuntimeStore } from './attachment-runtime.store';

describe('AttachmentPersistenceService', () => {
  let database: {
    isReady: ReturnType<typeof signal<boolean>>;
    getAllAttachments: ReturnType<typeof vi.fn>;
    getMessageById: ReturnType<typeof vi.fn>;
    saveAttachment: ReturnType<typeof vi.fn>;
    deleteAttachmentsForMessage: ReturnType<typeof vi.fn>;
  };
  let attachmentStorage: {
    resolveExistingPath: ReturnType<typeof vi.fn>;
    resolveCanonicalStoredPath: ReturnType<typeof vi.fn>;
    readFile: ReturnType<typeof vi.fn>;
    readFileChunk: ReturnType<typeof vi.fn>;
    getFileSize: ReturnType<typeof vi.fn>;
    canReadFileChunks: ReturnType<typeof vi.fn>;
  };

  beforeEach(() => {
    database = {
      isReady: signal(true),
      getAllAttachments: vi.fn(() => Promise.resolve([
        {
          id: 'att-1',
          messageId: 'msg-1',
          filename: 'photo.png',
          size: 1_500_000,
          mime: 'image/png',
          isImage: true,
          savedPath: '/appdata/photo.png'
        }
      ])),
      getMessageById: vi.fn(() => Promise.resolve(null)),
      saveAttachment: vi.fn(() => Promise.resolve()),
      deleteAttachmentsForMessage: vi.fn(() => Promise.resolve())
    };

    attachmentStorage = {
      resolveExistingPath: vi.fn(() => Promise.resolve('/appdata/photo.png')),
      resolveCanonicalStoredPath: vi.fn(() => Promise.resolve(null)),
      readFile: vi.fn(() => Promise.resolve('QUJD')),
      readFileChunk: vi.fn(() => Promise.resolve('QUJD')),
      getFileSize: vi.fn(() => Promise.resolve(3)),
      canReadFileChunks: vi.fn(() => true)
    };
  });

  function createService(): AttachmentPersistenceService {
    const injector = Injector.create({
      providers: [
        AttachmentPersistenceService,
        AttachmentRuntimeStore,
        { provide: DatabaseService, useValue: database },
        { provide: AttachmentStorageService, useValue: attachmentStorage },
        { provide: Store, useValue: { select: () => ({ pipe: () => ({ subscribe: () => {} }) }) } }
      ]
    });

    return runInInjectionContext(injector, () => injector.get(AttachmentPersistenceService));
  }

  it('loads attachment metadata at startup without eagerly hydrating blobs from disk', async () => {
    const service = createService();

    await service.initFromDatabase();

    expect(database.getAllAttachments).toHaveBeenCalledTimes(1);
    expect(attachmentStorage.readFile).not.toHaveBeenCalled();
    expect(attachmentStorage.readFileChunk).not.toHaveBeenCalled();
    expect(attachmentStorage.getFileSize).not.toHaveBeenCalled();
  });

  it('hydrates blob URLs on demand for a single attachment', async () => {
    const service = createService();

    await service.initFromDatabase();

    const attachment = {
      id: 'att-1',
      messageId: 'msg-1',
      filename: 'photo.png',
      size: 3,
      mime: 'image/png',
      isImage: true,
      savedPath: '/appdata/photo.png',
      available: false
    };

    await expect(service.ensureInlineDisplayObjectUrl(attachment)).resolves.toBe(true);
    expect(attachment.available).toBe(true);
    expect(attachment.objectUrl).toMatch(/^blob:/);
    expect(attachmentStorage.getFileSize).toHaveBeenCalledWith('/appdata/photo.png');
    expect(attachmentStorage.readFileChunk).toHaveBeenCalled();
    expect(attachmentStorage.readFile).not.toHaveBeenCalled();
  });
});
