import { Injectable, inject } from '@angular/core';
import { ElectronBridgeService } from '../../../../core/platform/electron/electron-bridge.service';
import type { Attachment } from '../../domain/models/attachment.model';
import {
  isAllowedAttachmentStoredPath,
  resolveAttachmentStorageBucket,
  resolveAttachmentStoredFilename,
  sanitizeAttachmentRoomName
} from '../util/attachment-storage.util';

const DIRECT_MESSAGE_STORAGE_PREFIX = 'direct-message:';

@Injectable({ providedIn: 'root' })
export class AttachmentStorageService {
  private readonly electronBridge = inject(ElectronBridgeService);

  canWriteFiles(): boolean {
    const electronApi = this.electronBridge.getApi();

    return !!electronApi?.appendFile && !!electronApi.writeFile && !!electronApi.getAppDataPath;
  }

  canReadFileChunks(): boolean {
    const electronApi = this.electronBridge.getApi();

    return !!electronApi?.readFileChunk && !!electronApi.getFileSize;
  }

  canCopyFiles(): boolean {
    const electronApi = this.electronBridge.getApi();

    return !!electronApi?.copyFile && !!electronApi.ensureDir && !!electronApi.getAppDataPath;
  }

  async resolveExistingPath(
    attachment: Pick<Attachment, 'filePath' | 'savedPath'>
  ): Promise<string | null> {
    return this.findExistingPath([attachment.savedPath, attachment.filePath]);
  }

  async resolveCanonicalStoredPath(
    attachment: Pick<Attachment, 'id' | 'filename' | 'mime'>,
    roomName: string
  ): Promise<string | null> {
    const appDataPath = await this.resolveAppDataPath();

    if (!appDataPath) {
      return null;
    }

    const directoryPath = this.resolveStorageDirectoryPath(appDataPath, roomName, attachment.mime);
    const diskPath = `${directoryPath}/${resolveAttachmentStoredFilename(attachment.id, attachment.filename)}`;

    return this.findExistingPath([diskPath]);
  }

  async copyFile(sourceFilePath: string, destinationFilePath: string): Promise<boolean> {
    const electronApi = this.electronBridge.getApi();

    if (!electronApi?.copyFile || !sourceFilePath || !destinationFilePath) {
      return false;
    }

    try {
      return await electronApi.copyFile(sourceFilePath, destinationFilePath);
    } catch {
      return false;
    }
  }

  async resolveLegacyImagePath(filename: string, roomName: string): Promise<string | null> {
    const appDataPath = await this.resolveAppDataPath();

    if (!appDataPath) {
      return null;
    }

    const safeFilename = resolveAttachmentStoredFilename('legacy', filename);

    return this.findExistingPath([`${appDataPath}/server/${sanitizeAttachmentRoomName(roomName)}/image/${safeFilename}`]);
  }

  async readFile(filePath: string): Promise<string | null> {
    const electronApi = this.electronBridge.getApi();

    if (!electronApi || !filePath) {
      return null;
    }

    try {
      return await electronApi.readFile(filePath);
    } catch {
      return null;
    }
  }

  async getFileSize(filePath: string): Promise<number | null> {
    const electronApi = this.electronBridge.getApi();

    if (!electronApi?.getFileSize || !filePath) {
      return null;
    }

    try {
      return await electronApi.getFileSize(filePath);
    } catch {
      return null;
    }
  }

  async readFileChunk(filePath: string, start: number, end: number): Promise<string | null> {
    const electronApi = this.electronBridge.getApi();

    if (!electronApi?.readFileChunk || !filePath) {
      return null;
    }

    try {
      return await electronApi.readFileChunk(filePath, start, end);
    } catch {
      return null;
    }
  }

  async getFileUrl(filePath: string): Promise<string | null> {
    const electronApi = this.electronBridge.getApi();

    if (!electronApi?.getFileUrl || !filePath) {
      return null;
    }

    try {
      return await electronApi.getFileUrl(filePath);
    } catch {
      return null;
    }
  }

  async saveBlob(
    attachment: Pick<Attachment, 'id' | 'filename' | 'mime'>,
    blob: Blob,
    roomName: string
  ): Promise<string | null> {
    const diskPath = await this.createWritableFile(attachment, roomName);

    if (!diskPath) {
      return null;
    }

    try {
      const arrayBuffer = await blob.arrayBuffer();

      await this.writeBase64(diskPath, this.arrayBufferToBase64(arrayBuffer));

      return diskPath;
    } catch {
      return null;
    }
  }

  async createWritableFile(
    attachment: Pick<Attachment, 'id' | 'filename' | 'mime'>,
    roomName: string
  ): Promise<string | null> {
    const electronApi = this.electronBridge.getApi();
    const appDataPath = await this.resolveAppDataPath();

    if (!electronApi || !appDataPath) {
      return null;
    }

    try {
      const directoryPath = this.resolveStorageDirectoryPath(appDataPath, roomName, attachment.mime);

      await electronApi.ensureDir(directoryPath);
      const diskPath = `${directoryPath}/${resolveAttachmentStoredFilename(attachment.id, attachment.filename)}`;

      await this.writeBase64(diskPath, '');

      return diskPath;
    } catch {
      return null;
    }
  }

  async appendBase64(filePath: string, base64Data: string): Promise<boolean> {
    const electronApi = this.electronBridge.getApi();

    if (!electronApi?.appendFile || !filePath) {
      return false;
    }

    try {
      return await electronApi.appendFile(filePath, base64Data);
    } catch {
      return false;
    }
  }

  async deleteFile(filePath: string): Promise<void> {
    const electronApi = this.electronBridge.getApi();

    if (!electronApi || !filePath) {
      return;
    }

    try {
      await electronApi.deleteFile(filePath);
    } catch { /* best-effort cleanup */ }
  }

  private async resolveAppDataPath(): Promise<string | null> {
    const electronApi = this.electronBridge.getApi();

    if (!electronApi) {
      return null;
    }

    try {
      return await electronApi.getAppDataPath();
    } catch {
      return null;
    }
  }

  private resolveStorageDirectoryPath(appDataPath: string, containerName: string, mime: string): string {
    const bucket = resolveAttachmentStorageBucket(mime);

    if (containerName.startsWith(DIRECT_MESSAGE_STORAGE_PREFIX)) {
      const conversationId = containerName.slice(DIRECT_MESSAGE_STORAGE_PREFIX.length);

      return `${appDataPath}/direct-messages/${sanitizeAttachmentRoomName(conversationId)}/${bucket}`;
    }

    return `${appDataPath}/server/${sanitizeAttachmentRoomName(containerName)}/${bucket}`;
  }

  private async findExistingPath(candidates: (string | null | undefined)[]): Promise<string | null> {
    const electronApi = this.electronBridge.getApi();
    const appDataPath = await this.resolveAppDataPath();

    if (!electronApi || !appDataPath) {
      return null;
    }

    for (const candidatePath of candidates) {
      if (!candidatePath || !isAllowedAttachmentStoredPath(candidatePath, appDataPath)) {
        continue;
      }

      try {
        if (await electronApi.fileExists(candidatePath)) {
          return candidatePath;
        }
      } catch { /* keep trying remaining candidates */ }
    }

    return null;
  }

  private async writeBase64(filePath: string, base64Data: string): Promise<boolean> {
    const electronApi = this.electronBridge.getApi();

    if (!electronApi || !filePath) {
      return false;
    }

    return await electronApi.writeFile(filePath, base64Data);
  }

  private arrayBufferToBase64(buffer: ArrayBuffer): string {
    let binary = '';

    const bytes = new Uint8Array(buffer);

    for (let index = 0; index < bytes.byteLength; index++) {
      binary += String.fromCharCode(bytes[index]);
    }

    return btoa(binary);
  }
}
