import {
  galleryRepository,
  mediaRepository,
  type MediaItem,
} from '@/lib/db/repositories/cmsRepository';
import { optimizeImageVariants } from '@/lib/media/optimize-image';
import {
  isMediaFolder,
  unlinkLocalUpload,
  writeImageVariantsToDisk,
  type MediaFolder,
} from '@/lib/media/storage-disk';
import { ALLOWED_IMAGE_MIMES, validateImageUpload } from '@/lib/upload-validation';

const MAX_ORIGINAL_NAME = 255;
const MAX_ALT_TEXT = 500;

export type MediaResult<T> =
  | { ok: true; data: T }
  | { ok: false; status: 404 | 422 | 500; error: string };

export type AddMediaInput = {
  buffer: Buffer;
  declaredMime: string;
  folder: string;
  originalName: string;
  altText?: string;
};

export type EditMediaInput = {
  id: number;
  buffer: Buffer;
  declaredMime: string;
  originalName: string;
  folder?: string;
  altText?: string;
};

function sanitizeOriginalName(name: string): string {
  const base = name.trim() || 'image';
  return base.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, MAX_ORIGINAL_NAME);
}

function sanitizeAltText(value: string | undefined): string | undefined {
  if (value === undefined) return undefined;
  return value.trim().slice(0, MAX_ALT_TEXT);
}

function parseFolder(value: string): MediaResult<MediaFolder> {
  if (!isMediaFolder(value)) {
    return { ok: false, status: 422, error: 'Ungültiger Ordner.' };
  }
  return { ok: true, data: value };
}

function parseMediaId(id: number): MediaResult<number> {
  if (!Number.isInteger(id) || id <= 0) {
    return { ok: false, status: 404, error: 'Medieneintrag nicht gefunden.' };
  }
  return { ok: true, data: id };
}

function asAllowedMime(mime: string): mime is (typeof ALLOWED_IMAGE_MIMES)[number] {
  return (ALLOWED_IMAGE_MIMES as readonly string[]).includes(mime);
}

export const mediaService = {
  /**
   * Validate, optimize (full/card/thumb WebP), write to disk, then insert media_library.
   * Rolls back all variant files if the DB insert fails.
   */
  async addItemToStorageDisk(input: AddMediaInput): Promise<MediaResult<MediaItem>> {
    const folderResult = parseFolder(input.folder);
    if (!folderResult.ok) return folderResult;

    const validation = validateImageUpload(input.buffer, input.declaredMime);
    if (!validation.ok) {
      return { ok: false, status: 422, error: validation.error };
    }
    if (!asAllowedMime(validation.mime)) {
      return { ok: false, status: 422, error: 'Nur Bilder (JPEG, PNG, WebP, GIF) sind erlaubt.' };
    }

    let variants: Awaited<ReturnType<typeof optimizeImageVariants>>;
    try {
      variants = await optimizeImageVariants(input.buffer, validation.mime);
    } catch (err) {
      console.error('[media] optimize failed', err instanceof Error ? err.message : 'unknown');
      return { ok: false, status: 500, error: 'Upload fehlgeschlagen.' };
    }

    let stored: Awaited<ReturnType<typeof writeImageVariantsToDisk>>;
    try {
      stored = await writeImageVariantsToDisk(folderResult.data, variants);
    } catch (err) {
      console.error('[media] write failed', err instanceof Error ? err.message : 'unknown');
      return { ok: false, status: 500, error: 'Upload fehlgeschlagen.' };
    }

    try {
      const item = await mediaRepository.create({
        filename: stored.filename,
        original_name: sanitizeOriginalName(input.originalName),
        url: stored.url,
        url_card: stored.url_card,
        url_thumb: stored.url_thumb,
        mime_type: variants.full.mime,
        file_size: stored.bytesWritten,
        alt_text: sanitizeAltText(input.altText),
        width: variants.full.width ?? undefined,
        height: variants.full.height ?? undefined,
        folder: folderResult.data,
      });

      if (!item) {
        await unlinkLocalUpload(stored.url);
        return { ok: false, status: 500, error: 'Upload fehlgeschlagen.' };
      }

      return { ok: true, data: item };
    } catch (err) {
      await unlinkLocalUpload(stored.url);
      console.error('[media] insert failed', err instanceof Error ? err.message : 'unknown');
      return { ok: false, status: 500, error: 'Upload fehlgeschlagen.' };
    }
  },

  /**
   * Delete the media row first, then unlink local /uploads files (full + variants).
   */
  async removeItemFromStorageDisk(id: number): Promise<MediaResult<{ success: true }>> {
    const idResult = parseMediaId(id);
    if (!idResult.ok) return idResult;

    const existing = await mediaRepository.findById(idResult.data);
    if (!existing) {
      return { ok: false, status: 404, error: 'Medieneintrag nicht gefunden.' };
    }

    const count = await mediaRepository.delete(idResult.data);
    if (count === 0) {
      return { ok: false, status: 404, error: 'Medieneintrag nicht gefunden.' };
    }

    try {
      await unlinkLocalUpload(existing.url);
    } catch {
      console.error('[media] failed to remove stored file after delete');
    }

    return { ok: true, data: { success: true } };
  },

  /**
   * Write the new file set, update the DB, then remove the previous local files.
   * If write or DB update fails, the old files and URL stay intact.
   */
  async editItemFromStorageDisk(input: EditMediaInput): Promise<MediaResult<MediaItem>> {
    const idResult = parseMediaId(input.id);
    if (!idResult.ok) return idResult;

    const existing = await mediaRepository.findById(idResult.data);
    if (!existing) {
      return { ok: false, status: 404, error: 'Medieneintrag nicht gefunden.' };
    }

    const folderResult = parseFolder(input.folder ?? existing.folder);
    if (!folderResult.ok) return folderResult;

    const validation = validateImageUpload(input.buffer, input.declaredMime);
    if (!validation.ok) {
      return { ok: false, status: 422, error: validation.error };
    }
    if (!asAllowedMime(validation.mime)) {
      return { ok: false, status: 422, error: 'Nur Bilder (JPEG, PNG, WebP, GIF) sind erlaubt.' };
    }

    let variants: Awaited<ReturnType<typeof optimizeImageVariants>>;
    try {
      variants = await optimizeImageVariants(input.buffer, validation.mime);
    } catch (err) {
      console.error(
        '[media] replace optimize failed',
        err instanceof Error ? err.message : 'unknown'
      );
      return { ok: false, status: 500, error: 'Medieneintrag konnte nicht aktualisiert werden.' };
    }

    let stored: Awaited<ReturnType<typeof writeImageVariantsToDisk>>;
    try {
      stored = await writeImageVariantsToDisk(folderResult.data, variants);
    } catch (err) {
      console.error('[media] replace write failed', err instanceof Error ? err.message : 'unknown');
      return { ok: false, status: 500, error: 'Medieneintrag konnte nicht aktualisiert werden.' };
    }

    try {
      const updated = await mediaRepository.replaceStoredFile(idResult.data, {
        filename: stored.filename,
        original_name: sanitizeOriginalName(input.originalName),
        url: stored.url,
        url_card: stored.url_card,
        url_thumb: stored.url_thumb,
        mime_type: variants.full.mime,
        file_size: stored.bytesWritten,
        alt_text: sanitizeAltText(input.altText),
        width: variants.full.width ?? undefined,
        height: variants.full.height ?? undefined,
        folder: folderResult.data,
      });

      if (!updated) {
        await unlinkLocalUpload(stored.url);
        return { ok: false, status: 404, error: 'Medieneintrag nicht gefunden.' };
      }

      try {
        await galleryRepository.updateUrlByExactMatch(existing.url, stored.url);
      } catch {
        console.error('[media] gallery URL update failed after replace');
      }

      if (existing.url !== stored.url) {
        await unlinkLocalUpload(existing.url);
      }

      return { ok: true, data: updated };
    } catch (err) {
      await unlinkLocalUpload(stored.url);
      console.error(
        '[media] replace update failed',
        err instanceof Error ? err.message : 'unknown'
      );
      return { ok: false, status: 500, error: 'Medieneintrag konnte nicht aktualisiert werden.' };
    }
  },

  async updateMediaMetadata(
    id: number,
    data: { alt_text?: string; folder?: string }
  ): Promise<MediaResult<MediaItem>> {
    const idResult = parseMediaId(id);
    if (!idResult.ok) return idResult;

    if (data.folder !== undefined) {
      const folderResult = parseFolder(data.folder);
      if (!folderResult.ok) return folderResult;
    }

    const item = await mediaRepository.update(idResult.data, {
      alt_text: sanitizeAltText(data.alt_text),
      folder: data.folder,
    });

    if (!item) {
      return { ok: false, status: 404, error: 'Medieneintrag nicht gefunden.' };
    }
    return { ok: true, data: item };
  },
};
