import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, IsNull } from 'typeorm';
import { ContentPiece, ContentStatus, ContentType } from '../entities/content-piece.entity';
import { CreateContentPieceDto, UpdateContentPieceDto } from '../dto';
import { TenantAwareService } from '@/shared/services/tenant-aware.service';
import { PaginationParams, PaginatedResult } from '@/shared/dto/pagination.dto';

@Injectable()
export class ContentPieceService extends TenantAwareService<ContentPiece> {
  constructor(
    @InjectRepository(ContentPiece)
    private readonly contentPieceRepository: Repository<ContentPiece>,
  ) {
    super(contentPieceRepository);
  }

  async findAllPaginated(
    tenantId: string,
    pagination: PaginationParams & {
      projectId?: string;
      type?: ContentType;
      status?: ContentStatus;
      search?: string;
    },
  ): Promise<PaginatedResult<ContentPiece>> {
    const {
      page = 1,
      limit = 20,
      sortBy = 'created_at',
      sortOrder = 'DESC',
      projectId,
      type,
      status,
      search,
    } = pagination;

    const queryBuilder = this.contentPieceRepository
      .createQueryBuilder('content')
      .leftJoinAndSelect('content.project', 'project')
      .leftJoinAndSelect('content.assignee', 'assignee')
      .where('content.tenant_id = :tenantId', { tenantId })
      .andWhere('content.deleted_at IS NULL');

    if (projectId) {
      queryBuilder.andWhere('content.project_id = :projectId', { projectId });
    }

    if (type) {
      queryBuilder.andWhere('content.type = :type', { type });
    }

    if (status) {
      queryBuilder.andWhere('content.status = :status', { status });
    }

    if (search) {
      queryBuilder.andWhere(
        '(content.title ILIKE :search OR content.content ILIKE :search)',
        { search: `%${search}%` },
      );
    }

    queryBuilder.orderBy(`content.${sortBy}`, sortOrder);

    const total = await queryBuilder.getCount();
    const totalPages = Math.ceil(total / limit);

    queryBuilder.skip((page - 1) * limit).take(limit);

    const data = await queryBuilder.getMany();

    return {
      data,
      meta: {
        total,
        page,
        limit,
        totalPages,
        hasNextPage: page < totalPages,
        hasPreviousPage: page > 1,
      },
    };
  }

  async findById(tenantId: string, id: string): Promise<ContentPiece> {
    const contentPiece = await this.contentPieceRepository.findOne({
      where: { id, tenant_id: tenantId, deleted_at: IsNull() },
      relations: ['project', 'assignee'],
    });

    if (!contentPiece) {
      throw new NotFoundException(`Content piece with ID ${id} not found`);
    }

    return contentPiece;
  }

  async findByProject(tenantId: string, projectId: string): Promise<ContentPiece[]> {
    return this.contentPieceRepository.find({
      where: { tenant_id: tenantId, project_id: projectId, deleted_at: IsNull() },
      relations: ['assignee'],
      order: { created_at: 'DESC' },
    });
  }

  async create(tenantId: string, dto: CreateContentPieceDto): Promise<ContentPiece> {
    const contentPiece = this.contentPieceRepository.create({
      ...dto,
      tenant_id: tenantId,
      version: 1,
    });

    return this.contentPieceRepository.save(contentPiece);
  }

  async update(tenantId: string, id: string, dto: UpdateContentPieceDto): Promise<ContentPiece> {
    const contentPiece = await this.findById(tenantId, id);

    // Increment version on content changes
    if (dto.content && dto.content !== contentPiece.content) {
      contentPiece.version += 1;
    }

    Object.assign(contentPiece, dto);

    return this.contentPieceRepository.save(contentPiece);
  }

  async updateStatus(tenantId: string, id: string, status: ContentStatus): Promise<ContentPiece> {
    const contentPiece = await this.findById(tenantId, id);
    contentPiece.status = status;

    if (status === ContentStatus.PUBLISHED) {
      contentPiece.published_at = new Date();
    }

    return this.contentPieceRepository.save(contentPiece);
  }

  async assignTo(tenantId: string, id: string, userId: string | null): Promise<ContentPiece> {
    const contentPiece = await this.findById(tenantId, id);
    contentPiece.assigned_to = userId;
    return this.contentPieceRepository.save(contentPiece);
  }

  async schedule(tenantId: string, id: string, scheduledAt: Date): Promise<ContentPiece> {
    const contentPiece = await this.findById(tenantId, id);
    contentPiece.scheduled_at = scheduledAt;
    contentPiece.status = ContentStatus.SCHEDULED;
    return this.contentPieceRepository.save(contentPiece);
  }

  async publish(tenantId: string, id: string, publishedUrl?: string): Promise<ContentPiece> {
    const contentPiece = await this.findById(tenantId, id);
    contentPiece.status = ContentStatus.PUBLISHED;
    contentPiece.published_at = new Date();
    if (publishedUrl) {
      contentPiece.published_url = publishedUrl;
    }
    return this.contentPieceRepository.save(contentPiece);
  }

  async softDelete(tenantId: string, id: string): Promise<void> {
    const contentPiece = await this.findById(tenantId, id);
    contentPiece.deleted_at = new Date();
    await this.contentPieceRepository.save(contentPiece);
  }

  async duplicate(tenantId: string, id: string): Promise<ContentPiece> {
    const original = await this.findById(tenantId, id);

    const duplicate = this.contentPieceRepository.create({
      ...original,
      id: undefined,
      title: `${original.title} (Copy)`,
      status: ContentStatus.IDEA,
      published_at: null,
      published_url: null,
      scheduled_at: null,
      version: 1,
      created_at: undefined,
      updated_at: undefined,
    });

    return this.contentPieceRepository.save(duplicate);
  }
}
