import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BlogPost, BlogPostCategory, BlogPostStatus } from '@domain/entities/blog-post.entity';
import { BlogPostFilters, BlogPostRepository } from '@domain/ports/out/blog-post.repository';
import { BlogPostOrmEntity } from '../entities/blog-post.orm-entity';

@Injectable()
export class TypeOrmBlogPostRepository implements BlogPostRepository {
  constructor(
    @InjectRepository(BlogPostOrmEntity)
    private readonly ormRepository: Repository<BlogPostOrmEntity>
  ) {}

  async save(post: BlogPost): Promise<BlogPost> {
    const orm = this.toOrm(post);
    const saved = await this.ormRepository.save(orm);
    return this.toDomain(saved);
  }

  async findById(id: string): Promise<BlogPost | null> {
    const orm = await this.ormRepository.findOne({ where: { id } });
    return orm ? this.toDomain(orm) : null;
  }

  async findBySlug(slug: string): Promise<BlogPost | null> {
    const orm = await this.ormRepository.findOne({ where: { slug } });
    return orm ? this.toDomain(orm) : null;
  }

  async findByFilters(filters: BlogPostFilters): Promise<BlogPost[]> {
    const query = this.ormRepository.createQueryBuilder('post');

    if (filters.status) {
      query.andWhere('post.status = :status', { status: filters.status });
    }

    if (filters.category) {
      query.andWhere('post.category = :category', { category: filters.category });
    }

    if (filters.isFeatured !== undefined) {
      query.andWhere('post.is_featured = :isFeatured', { isFeatured: filters.isFeatured });
    }

    if (filters.search) {
      query.andWhere('(post.title ILIKE :search OR post.excerpt ILIKE :search)', {
        search: `%${filters.search}%`,
      });
    }

    query.orderBy('post.published_at', 'DESC').addOrderBy('post.created_at', 'DESC');

    if (filters.offset) query.skip(filters.offset);
    if (filters.limit) query.take(filters.limit);

    const results = await query.getMany();
    return results.map(e => this.toDomain(e));
  }

  async count(filters: BlogPostFilters): Promise<number> {
    const query = this.ormRepository.createQueryBuilder('post');

    if (filters.status) {
      query.andWhere('post.status = :status', { status: filters.status });
    }
    if (filters.category) {
      query.andWhere('post.category = :category', { category: filters.category });
    }
    if (filters.search) {
      query.andWhere('(post.title ILIKE :search OR post.excerpt ILIKE :search)', {
        search: `%${filters.search}%`,
      });
    }

    return query.getCount();
  }

  async delete(id: string): Promise<void> {
    await this.ormRepository.delete(id);
  }

  async slugExists(slug: string, excludeId?: string): Promise<boolean> {
    const query = this.ormRepository
      .createQueryBuilder('post')
      .where('post.slug = :slug', { slug });
    if (excludeId) {
      query.andWhere('post.id != :excludeId', { excludeId });
    }
    const count = await query.getCount();
    return count > 0;
  }

  private toDomain(orm: BlogPostOrmEntity): BlogPost {
    return BlogPost.fromPersistence({
      id: orm.id,
      title: orm.title,
      slug: orm.slug,
      excerpt: orm.excerpt,
      content: orm.content,
      coverImageUrl: orm.cover_image_url,
      category: orm.category as BlogPostCategory,
      tags: orm.tags ?? [],
      authorName: orm.author_name,
      status: orm.status as BlogPostStatus,
      isFeatured: orm.is_featured,
      publishedAt: orm.published_at,
      createdAt: orm.created_at,
      updatedAt: orm.updated_at,
    });
  }

  private toOrm(post: BlogPost): BlogPostOrmEntity {
    const orm = new BlogPostOrmEntity();
    orm.id = post.id;
    orm.title = post.title;
    orm.slug = post.slug;
    orm.excerpt = post.excerpt;
    orm.content = post.content;
    orm.cover_image_url = post.coverImageUrl;
    orm.category = post.category;
    orm.tags = post.tags;
    orm.author_name = post.authorName;
    orm.status = post.status;
    orm.is_featured = post.isFeatured;
    orm.published_at = post.publishedAt;
    orm.created_at = post.createdAt;
    orm.updated_at = post.updatedAt;
    return orm;
  }
}
