import { Injectable, inject } from '@angular/core';
import { ElectronBridgeService } from '../../../../core/platform/electron/electron-bridge.service';
import type { SavedThemeFileDescriptor } from '../../../../core/platform/electron/electron-api.models';
import type { SavedThemeSummary } from '../../domain/models/theme.model';
import { validateThemeDocument } from '../../domain/logic/theme-validation.logic';

const THEME_LIBRARY_REQUEST_TIMEOUT_MS = 4000;

function sanitizeSavedThemeStem(value: string): string {
  const normalized = value
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '');

  return normalized.length > 0
    ? normalized
    : 'theme';
}

function buildSavedThemeFileName(stem: string, suffix?: number): string {
  return suffix && suffix > 1
    ? `${stem}-${suffix}.json`
    : `${stem}.json`;
}

function fallbackThemeName(fileName: string): string {
  return fileName.replace(/\.json$/i, '');
}

async function withTimeout<T>(operation: Promise<T>, label: string): Promise<T> {
  let timeoutId: ReturnType<typeof setTimeout> | null = null;

  try {
    return await Promise.race([
      operation,
      new Promise<T>((_resolve, reject) => {
        timeoutId = setTimeout(() => {
          reject(new Error(`${label} timed out.`));
        }, THEME_LIBRARY_REQUEST_TIMEOUT_MS);
      })
    ]);
  } finally {
    if (timeoutId) {
      clearTimeout(timeoutId);
    }
  }
}

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

  get isAvailable(): boolean {
    return this.electronBridge.isAvailable;
  }

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

    if (!electronApi) {
      return null;
    }

    try {
      return await withTimeout(electronApi.getSavedThemesPath(), 'Loading the saved themes path');
    } catch {
      return null;
    }
  }

  async listThemes(): Promise<SavedThemeSummary[]> {
    const electronApi = this.electronBridge.getApi();

    if (!electronApi) {
      return [];
    }

    try {
      const files = await withTimeout(electronApi.listSavedThemes(), 'Loading saved themes');
      const summaries = await Promise.all(files.map(async (file) => await this.readSavedThemeSummary(file)));

      return summaries.sort((left, right) => {
        const nameOrder = left.themeName.localeCompare(right.themeName, undefined, { sensitivity: 'base' });

        return nameOrder !== 0
          ? nameOrder
          : right.modifiedAt - left.modifiedAt;
      });
    } catch {
      return [];
    }
  }

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

    if (!electronApi) {
      return null;
    }

    try {
      return await withTimeout(electronApi.readSavedTheme(fileName), `Reading saved theme ${fileName}`);
    } catch {
      return null;
    }
  }

  async saveNewTheme(preferredName: string, text: string): Promise<string | null> {
    const electronApi = this.electronBridge.getApi();

    if (!electronApi) {
      return null;
    }

    try {
      const existingFiles = await withTimeout(electronApi.listSavedThemes(), 'Loading saved themes');
      const existingNames = new Set(existingFiles.map((file) => file.fileName.toLowerCase()));
      const stem = sanitizeSavedThemeStem(preferredName);

      let suffix = 1;
      let fileName = buildSavedThemeFileName(stem);

      while (existingNames.has(fileName.toLowerCase())) {
        suffix += 1;
        fileName = buildSavedThemeFileName(stem, suffix);
      }

      await withTimeout(electronApi.writeSavedTheme(fileName, text), `Saving theme ${fileName}`);

      return fileName;
    } catch {
      return null;
    }
  }

  async overwriteTheme(fileName: string, text: string): Promise<boolean> {
    const electronApi = this.electronBridge.getApi();

    if (!electronApi) {
      return false;
    }

    try {
      return await withTimeout(electronApi.writeSavedTheme(fileName, text), `Saving theme ${fileName}`);
    } catch {
      return false;
    }
  }

  async deleteTheme(fileName: string): Promise<boolean> {
    const electronApi = this.electronBridge.getApi();

    if (!electronApi) {
      return false;
    }

    try {
      return await withTimeout(electronApi.deleteSavedTheme(fileName), `Deleting theme ${fileName}`);
    } catch {
      return false;
    }
  }

  private async readSavedThemeSummary(file: SavedThemeFileDescriptor): Promise<SavedThemeSummary> {
    const text = await this.readThemeText(file.fileName);

    if (!text) {
      return {
        description: null,
        error: 'File could not be read.',
        fileName: file.fileName,
        filePath: file.path,
        isValid: false,
        modifiedAt: file.modifiedAt,
        themeName: fallbackThemeName(file.fileName),
        version: null
      };
    }

    try {
      const parsed = JSON.parse(text) as unknown;
      const result = validateThemeDocument(parsed);

      if (!result.valid || !result.value) {
        return {
          description: null,
          error: result.errors[0] ?? 'Theme JSON is invalid.',
          fileName: file.fileName,
          filePath: file.path,
          isValid: false,
          modifiedAt: file.modifiedAt,
          themeName: fallbackThemeName(file.fileName),
          version: null
        };
      }

      return {
        description: result.value.meta.description ?? null,
        error: null,
        fileName: file.fileName,
        filePath: file.path,
        isValid: true,
        modifiedAt: file.modifiedAt,
        themeName: result.value.meta.name,
        version: result.value.meta.version
      };
    } catch (error) {
      return {
        description: null,
        error: error instanceof Error ? error.message : 'Theme JSON could not be parsed.',
        fileName: file.fileName,
        filePath: file.path,
        isValid: false,
        modifiedAt: file.modifiedAt,
        themeName: fallbackThemeName(file.fileName),
        version: null
      };
    }
  }
}
