import {
  Controller,
  Get,
  Param,
  Query,
  HttpCode,
  HttpStatus,
  Res,
  NotFoundException,
  Inject,
  Logger,
  StreamableFile,
} from '@nestjs/common';
import { Response } from 'express';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger';
import { Public } from '../decorators/public.decorator';
import { BlogService } from '../services/blog.service';
import { BlogPost } from '@domain/entities/blog-post.entity';
import { BlogPostResponseDto, BlogPostListResponseDto } from '../dto/blog-post.dto';
import type { BlogPostCategory } from '@domain/entities/blog-post.entity';
import { StoragePort, STORAGE_PORT } from '@domain/ports/out/storage.port';

const BLOG_IMAGES_BUCKET = 'xpeditis-blog';

@ApiTags('Blog')
@Controller('blog')
@Public()
export class BlogController {
  private readonly logger = new Logger(BlogController.name);

  constructor(
    private readonly blogService: BlogService,
    @Inject(STORAGE_PORT) private readonly storage: StoragePort
  ) {}

  @Get()
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'List published blog posts' })
  @ApiQuery({
    name: 'category',
    required: false,
    enum: ['industry', 'technology', 'guides', 'news'],
  })
  @ApiQuery({ name: 'search', required: false })
  @ApiQuery({ name: 'limit', required: false, type: Number })
  @ApiQuery({ name: 'offset', required: false, type: Number })
  @ApiResponse({ status: 200, type: BlogPostListResponseDto })
  async listPosts(
    @Query('category') category?: BlogPostCategory,
    @Query('search') search?: string,
    @Query('limit') limit = 20,
    @Query('offset') offset = 0
  ): Promise<BlogPostListResponseDto> {
    const { posts, total } = await this.blogService.listPublishedPosts({
      category,
      search,
      limit: Number(limit),
      offset: Number(offset),
    });

    return {
      posts: posts.map(this.mapToDto),
      total,
      limit: Number(limit),
      offset: Number(offset),
    };
  }

  @Get('images/:filename')
  @ApiOperation({ summary: 'Serve a blog image from storage' })
  @ApiParam({ name: 'filename' })
  async serveImage(
    @Param('filename') filename: string,
    @Res({ passthrough: true }) res: Response
  ): Promise<StreamableFile> {
    const key = `blog-images/${filename}`;

    let buffer: Buffer;
    try {
      buffer = await this.storage.download({ bucket: BLOG_IMAGES_BUCKET, key });
    } catch (err: any) {
      this.logger.error(`Failed to serve blog image "${key}": ${err?.message}`);
      throw new NotFoundException(`Image not found: ${filename}`);
    }

    const ext = filename.split('.').pop()?.toLowerCase() ?? '';
    const contentTypeMap: Record<string, string> = {
      jpg: 'image/jpeg',
      jpeg: 'image/jpeg',
      png: 'image/png',
      webp: 'image/webp',
      gif: 'image/gif',
      svg: 'image/svg+xml',
    };
    const contentType = contentTypeMap[ext] ?? 'application/octet-stream';

    res.setHeader('Content-Type', contentType);
    res.setHeader('Cache-Control', 'public, max-age=3600');
    res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
    return new StreamableFile(buffer);
  }

  @Get(':slug')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Get a published blog post by slug' })
  @ApiParam({ name: 'slug' })
  @ApiResponse({ status: 200, type: BlogPostResponseDto })
  async getPost(@Param('slug') slug: string): Promise<BlogPostResponseDto> {
    const post = await this.blogService.getPublishedPostBySlug(slug);
    return this.mapToDto(post);
  }

  private mapToDto(post: BlogPost): BlogPostResponseDto {
    return {
      id: post.id,
      title: post.title,
      slug: post.slug,
      excerpt: post.excerpt,
      content: post.content,
      coverImageUrl: post.coverImageUrl,
      category: post.category,
      tags: post.tags,
      authorName: post.authorName,
      status: post.status,
      isFeatured: post.isFeatured,
      publishedAt: post.publishedAt,
      createdAt: post.createdAt,
      updatedAt: post.updatedAt,
    };
  }
}
