import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, IsNull } from 'typeorm';
import { Asset, AssetType, AssetStatus } from '../entities/asset.entity';
import { CreateAssetDto, UpdateAssetDto } from '../dto';
import { TenantAwareService } from '@/shared/services/tenant-aware.service';
import { PaginationParams, PaginatedResult } from '@/shared/dto/pagination.dto';

@Injectable()
export class AssetService extends TenantAwareService<Asset> {
  constructor(
    @InjectRepository(Asset)
    private readonly assetRepository: Repository<Asset>,
  ) {
    super(assetRepository);
  }

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

    const queryBuilder = this.assetRepository
      .createQueryBuilder('asset')
      .leftJoinAndSelect('asset.brand', 'brand')
      .where('asset.tenant_id = :tenantId', { tenantId })
      .andWhere('asset.deleted_at IS NULL');

    if (brandId) {
      queryBuilder.andWhere('asset.brand_id = :brandId', { brandId });
    }

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

    if (search) {
      queryBuilder.andWhere(
        '(asset.name ILIKE :search OR asset.original_name ILIKE :search OR asset.alt_text ILIKE :search)',
        { search: `%${search}%` },
      );
    }

    queryBuilder.orderBy(`asset.${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<Asset> {
    const asset = await this.assetRepository.findOne({
      where: { id, tenant_id: tenantId, deleted_at: IsNull() },
      relations: ['brand'],
    });

    if (!asset) {
      throw new NotFoundException(`Asset with ID ${id} not found`);
    }

    return asset;
  }

  async findByBrand(tenantId: string, brandId: string): Promise<Asset[]> {
    return this.assetRepository.find({
      where: { tenant_id: tenantId, brand_id: brandId, deleted_at: IsNull() },
      order: { created_at: 'DESC' },
    });
  }

  async create(tenantId: string, dto: CreateAssetDto): Promise<Asset> {
    const asset = this.assetRepository.create({
      ...dto,
      tenant_id: tenantId,
    });

    return this.assetRepository.save(asset);
  }

  async update(tenantId: string, id: string, dto: UpdateAssetDto): Promise<Asset> {
    const asset = await this.findById(tenantId, id);

    Object.assign(asset, dto);

    return this.assetRepository.save(asset);
  }

  async updateStatus(tenantId: string, id: string, status: AssetStatus): Promise<Asset> {
    const asset = await this.findById(tenantId, id);
    asset.status = status;
    return this.assetRepository.save(asset);
  }

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

  async hardDelete(tenantId: string, id: string): Promise<void> {
    const asset = await this.findById(tenantId, id);
    await this.assetRepository.remove(asset);
  }

  async bulkDelete(tenantId: string, ids: string[]): Promise<void> {
    await this.assetRepository
      .createQueryBuilder()
      .update(Asset)
      .set({ deleted_at: new Date() })
      .where('tenant_id = :tenantId', { tenantId })
      .andWhere('id IN (:...ids)', { ids })
      .execute();
  }

  getAssetTypeFromMimeType(mimeType: string): AssetType {
    if (mimeType.startsWith('image/')) return AssetType.IMAGE;
    if (mimeType.startsWith('video/')) return AssetType.VIDEO;
    if (mimeType.startsWith('audio/')) return AssetType.AUDIO;
    if (
      mimeType.startsWith('application/pdf') ||
      mimeType.startsWith('application/msword') ||
      mimeType.startsWith('application/vnd.')
    ) {
      return AssetType.DOCUMENT;
    }
    return AssetType.OTHER;
  }
}
