/**
 * Use Case: UploadMediaUseCase
 *
 * Handles the business logic for uploading media files.
 */

import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { v4 as uuidv4 } from 'uuid';
import { Media } from '../../domain/entities/Media';
import { MediaRepository } from '../../domain/repositories/MediaRepository';
import type { MediaStoragePort } from '../ports/MediaStoragePort';

// Define Multer file type locally since @types/multer is not available
export interface MulterFile {
  fieldname: string;
  originalname: string;
  encoding: string;
  mimetype: string;
  size: number;
  buffer: Buffer;
  stream: NodeJS.ReadableStream;
  destination: string;
  filename: string;
  path: string;
}

export interface UploadMediaInput {
  file: MulterFile;
  uploadedBy: string;
  metadata?: Record<string, unknown>;
}

export interface UploadMediaResult {
  mediaId: string;
  url: string | undefined;
}

export type UploadMediaErrorCode =
  | 'UPLOAD_FAILED'
  | 'REPOSITORY_ERROR';

export class UploadMediaUseCase {
  constructor(
    private readonly mediaRepo: MediaRepository,
    private readonly mediaStorage: MediaStoragePort,
    private readonly logger: Logger,
  ) {}

  async execute(
    input: UploadMediaInput,
  ): Promise<Result<UploadMediaResult, ApplicationErrorCode<UploadMediaErrorCode, { message: string }>>> {
    this.logger.info('[UploadMediaUseCase] Starting media upload', {
      filename: input.file.originalname,
      size: input.file.size,
      uploadedBy: input.uploadedBy,
    });

    try {
      // Upload file to storage service
      const uploadOptions: {
        filename: string;
        mimeType: string;
        metadata?: Record<string, unknown>;
      } = {
        filename: input.file.originalname,
        mimeType: input.file.mimetype,
      };
      if (input.metadata !== undefined) {
        uploadOptions.metadata = input.metadata;
      }
      const uploadResult = await this.mediaStorage.uploadMedia(input.file.buffer, uploadOptions);

      if (!uploadResult.success || !uploadResult.url) {
        return Result.err<UploadMediaResult, ApplicationErrorCode<UploadMediaErrorCode, { message: string }>>({
          code: 'UPLOAD_FAILED',
          details: {
            message:
              uploadResult.errorMessage ?? 'Failed to upload media',
          },
        });
      }

      // Determine media type
      const mediaType: 'image' | 'video' | 'document' = input.file.mimetype.startsWith('image/')
        ? 'image'
        : input.file.mimetype.startsWith('video/')
        ? 'video'
        : 'document';

      // Create media entity
      const mediaId = uuidv4();
      const mediaProps: Parameters<typeof Media.create>[0] = {
        id: mediaId,
        filename: uploadResult.filename || input.file.originalname,
        originalName: input.file.originalname,
        mimeType: input.file.mimetype,
        size: input.file.size,
        url: uploadResult.url,
        type: mediaType,
        uploadedBy: input.uploadedBy,
      };
      
      if (input.metadata !== undefined) {
        mediaProps.metadata = input.metadata;
      }
      
      const media = Media.create(mediaProps);

      // Save to repository
      await this.mediaRepo.save(media);

      const result: UploadMediaResult = {
        mediaId,
        url: uploadResult.url,
      };

      this.logger.info('[UploadMediaUseCase] Media uploaded successfully', {
        mediaId,
        url: uploadResult.url,
      });

      return Result.ok(result);
    } catch (error) {
      const err = error instanceof Error ? error : new Error(String(error));
      this.logger.error('[UploadMediaUseCase] Error uploading media', err, {
        filename: input.file.originalname,
      });

      return Result.err<UploadMediaResult, ApplicationErrorCode<UploadMediaErrorCode, { message: string }>>({
        code: 'REPOSITORY_ERROR',
        details: { message: err.message },
      });
    }
  }
}